Compare commits

...
56 Commits
Author SHA1 Message Date
Adam Honse 227fd375fb Rework show/hide to reduce the surface called from the SignalUpdate callback 2026-07-27 23:14:39 -05:00
Alessandro Di Ronza 069eeccb60 CLI: return a device list again, and print the version 2026-07-27 17:27:54 -05:00
Alessandro Di Ronza da77b63bea Fix SteelSeries Apex 9 lighting by using the correct direct report size 2026-07-27 17:23:28 -05:00
Adam Honse 791d8820d6 Get active effect and active settings during Valve Steam Machine initialization 2026-07-26 23:18:15 -05:00
Ken Sanislo 05fbe381dd MSIMonitorController: idle the control pipe between feature reports 2026-07-26 22:07:33 -05:00
isJuhn 3b2466ba10 Fix use after free in NetworkClient and NetworkServer 2026-07-26 22:03:43 -05:00
Ken Sanislo acc7fa6754 Invoke resource manager callbacks outside the callback mutex 2026-07-26 17:47:18 -05:00
Adam Honse 394a5c8eb0 Replace thread-local SignalUpdateDepth with SignalCalls per controller. Fixes an issue where callbacks that performed an unregister or clear operation on a separate thread would still deadlock, as is the case in Visual Map Plugin. Cleaned up comments 2026-07-26 01:29:34 -05:00
Adam Honse ac6d89b611 Bundle Qt6OpenGL and Qt6Core5Compat libraries and adjust LD_LIBRARY_PATH in AppImage to fix plugins using the system qt6 libraries 2026-07-25 21:37:03 -05:00
Ilya Miller 46cdd6fbe1 OpenRGBDevicePage: keep the color selection when applying a color 2026-07-25 13:28:38 -05:00
Adam Honse 14d543b9e4 Initial commit for Linux-only controller for Valve Steam Machine using
the leds-valve kernel module and sysfs interface
2026-07-25 01:16:11 -05:00
Ilya Miller 8a831cef04 Add Dark Project KD104A support to the Keyrox Classic driver 2026-07-24 13:53:06 -05:00
Ken Sanislo 9fd79b1ad2 RGBController_Virtual: signal update when description is replaced 2026-07-24 13:22:00 -05:00
Daniel Clark c9c37a4576 Add GC-USB dongle, per zone effects, Gen2 Zones, and Updated MB Layouts 2026-07-22 22:47:25 -05:00
Ken Sanislo fe53d90664 RGBController: fix callback list race and dangling device-page callback on rescan 2026-07-22 21:59:30 -05:00
Adam Honse 8e8a9c8e05 Split Alienware keyboard controller detect file into individual controllers 2026-07-22 19:08:23 -05:00
Adam Honse dafc6e9cbc Split Bloody/A4Tech detector file into individual files per controller 2026-07-22 11:37:15 -05:00
Bernd Loecke 68b113bf86 Add MSI MAG X870E TOMAHAWK MAX WIFI PZ support 2026-07-22 07:42:41 -05:00
Ken Sanislo eb5d8b579c MSIMysticLight761: restore resizable JAF/JARGB zones 2026-07-22 07:41:30 -05:00
Adam Honse 56df76dbfc Split HyperX keyboard detector file into individual detector files 2026-07-22 00:42:16 -05:00
Adam Honse 878f32fccf Split Holtek detector file into individual detector files for A1FA Mousemat and A070 Mouse 2026-07-21 23:50:13 -05:00
Adam Honse 63828313bd Split EVGAUSBControllerDetect into individual keyboard and mouse files 2026-07-21 23:34:06 -05:00
homelab black 31992acad1 Fix layout regression for KEY_EN_ESCAPE and KEY_JP_EJ 2026-07-21 22:44:15 +09:00
HNParda bd41ba3b5c Added Support for Gigabyte RX 6700 XT Eagle 12G 2026-07-21 00:15:58 -05:00
Adam Honse b9309c61e1 Split EVision keyboard files into folders and split the shared detector file 2026-07-21 00:14:12 -05:00
Ken Sanislo 7fc8aea930 MSIMonitorController: 0x72 layout support (MPG 322URX QD-OLED + siblings) 2026-07-20 23:22:02 -05:00
Ken Sanislo b35e119325 DetectionManager: controller lifecycle and thread-safe registration 2026-07-19 02:04:46 -05:00
eldiablo 8fb790358f Add support for SteelSeries Arctis Nova 3 headset 2026-07-18 18:54:38 -05:00
Ken Sanislo 39c6dbf361 DeviceView: add F13-F15 and Number Pad = device-view labels 2026-07-18 13:01:25 -05:00
John Hamlin 9d189d5b9b Add support for MSI B850 GAMING PLUS WIFI PZ (MS-7E75) motherboard 2026-07-17 19:07:39 -05:00
Adam Honse 9e5c63cc64 Add second sub-dev for MSI RTX4070Ti Super Gaming White X Slim 2026-07-17 18:54:54 -05:00
Adam Honse b664f35e17 Fix potential out of bounds reference in ASRockPolychromeUSB controller when zone size is 0 2026-07-17 17:17:39 -05:00
Filip Machałowski 381e3ac70c Add support for PowerColor Red Devil RX 9070 2026-07-17 13:19:35 +02:00
Stella e3225d93c4 Add support for Colorful iGAME RTX 5070Ti Ultra W OC 16GB 2026-07-17 00:14:57 -05:00
Andrey 1b3480bf8a Replace ADL2_ADAPTER_PRIMARY_GET function usage with adapter_index in i2c_smbus_amdadl 2026-07-16 08:17:16 -05:00
RedBlackAka a6bea17df2 Add Nvidia RTX 2080 Ti Cyperpunk 2077 Edition 2026-07-16 04:10:41 +02:00
Adam Honse 28d73d74bc Delete hidden pages if their controller no longer exists 2026-07-14 21:45:18 -05:00
Adam Honse cf383cacdd Use a blocking connection when updating the device list to prevent accessing deleted controllers 2026-07-14 21:45:18 -05:00
Adam Honse d9b0995b44 Run RescanDevices in a background thread to avoid locking the UI thread and causing a deadlock in the plugins 2026-07-14 21:45:18 -05:00
Ken Sanislo 79eb758a52 Logitech HID++ 2.0: restore COUNT fill for the keyboard matrix map 2026-07-14 21:45:18 -05:00
Daniel Clark 75463b8772 Fix K5 V2 layout and add ANSI Enter Expansion 2026-07-14 21:45:18 -05:00
Barry H 5da92fca4e Added all Skydimo Serial and HID devices 2026-07-14 21:42:42 -05:00
Darren You d11be2aa0b Initial commit for Colorful iGame GeForce RTX 4090 Neptune OC-V 2026-07-13 00:17:06 -05:00
Adam Honse 285a7bce4e Implement a matrix patching system in the Keychron QMK controller to apply keyboard-specific patches to correct for misplaced LEDs 2026-07-12 16:25:07 -05:00
Adam Honse 21009cdc45 Don't send RGBController packets that are too new for the current protocol in NetworkClient 2026-07-11 14:20:43 -05:00
Adam Honse a0cee2ce2b Maintain a list of created virtual controllers in the OpenRGBPluginAPI object and only allow registering virtual controllers that exist in this list 2026-07-11 14:20:38 -05:00
Joseph E ba33ff0c35 Steelseries Apex 9 merge into Apex controller + enable onboard mode 2026-07-11 00:11:45 -05:00
compguy284 795616e62d Fix Corsair keyboard cold-boot detection by retrying firmware info read 2026-07-10 17:55:23 -05:00
Adam Honse baa033ff7f Add Keychron Q0 HE 2026-07-10 15:40:35 -05:00
Eder Sanchez 33ec03d129 NetworkServer: reject SDK index == element count (off-by-one)
The SDK LED/zone update handlers checked the client-supplied index with '>'
instead of '>=', so an index equal to the element count passed the check and
reached an out-of-bounds access:

- UpdateSingleLED: out-of-bounds write to colors[led_idx]
- UpdateZoneLEDs / UpdateZoneMode: out-of-bounds read/write on zones[zone_idx]

UpdateMode had the same off-by-one (currently harmless due to a later check),
fixed for consistency. The num_colors check stays '>' since num_colors ==
leds_count is a valid "all LEDs" request.
2026-07-10 14:44:44 -05:00
Eder Sanchez a0d5a985f0 ZOTAC Blackwell GPU: fix firmware-version read
ReadVersion() issued a register-less block read, so the device returned a
status byte (0x01) instead of the version string. Read register 0xF0 (a
32-byte ASCII board string), as the vendor tool does, and validate it.

- fixes the empty/garbage firmware version in the log and GUI
- closes an out-of-bounds read (unbounded std::string over a 32-byte buffer)
- tested on RTX 5080 AMP Extreme INFINITY: now reads "N762A-2008e"
2026-07-10 13:42:01 -05:00
Adam Honse 1275fe816b Make tray icon menus translatable 2026-07-10 13:00:12 -05:00
homelab black d4e1ed5245 Controllers: RoccatVulcan: Add Japanese (JIS) layout support for Vulcan II Max 2026-07-10 00:14:02 -05:00
Marius Huecker b833a43490 Add Gainward RTX 4080 Phantom GS 2026-07-08 20:16:38 -05:00
Adam Honse 94295c52ba Dynamically set DeviceView minimum height based on height of contents to avoid devices with a lot of segments/zones getting squished 2026-07-08 20:05:21 -05:00
Adam Honse f32aa29ecd Put Device View in a QScrollArea so it can be scrolled if it is too large to fit in the window 2026-07-08 11:03:31 -05:00
140 changed files with 8484 additions and 2654 deletions
@@ -1,66 +0,0 @@
/*---------------------------------------------------------*\
| A4TechDetector.cpp |
| |
| Detector for A4Tech Devices |
| |
| Chris M (Dr_No) 30 Jun 2022 |
| Mohammed Julfikar Ali Mahbub (o-julfikar) 01 Apr 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "BloodyB820RController.h"
#include "BloodyMouseController.h"
#include "DetectionManager.h"
#include "RGBController_BloodyB820R.h"
#include "RGBController_BloodyMouse.h"
/*---------------------------------------------------------*\
| A4 Tech USB vendor ID |
\*---------------------------------------------------------*/
#define A4_TECH_VID 0x09DA
DetectedControllers DetectA4TechMouseControllers(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
BloodyMouseController* controller = new BloodyMouseController(dev, info->path, info->product_id, name);
RGBController_BloodyMouse* rgb_controller = new RGBController_BloodyMouse(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectBloodyB820R(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
BloodyB820RController* controller = new BloodyB820RController(dev, info->path, name);
RGBController_BloodyB820R* rgb_controller = new RGBController_BloodyB820R(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Bloody W60 Pro", DetectA4TechMouseControllers, A4_TECH_VID, BLOODY_W60_PRO_PID, 2, 0xFF33, 0x0529);
REGISTER_HID_DETECTOR_IPU("Bloody W70 Max", DetectA4TechMouseControllers, A4_TECH_VID, BLOODY_W70_MAX_PID, 2, 0xFF33, 0x0518);
REGISTER_HID_DETECTOR_IPU("Bloody W90 Max", DetectA4TechMouseControllers, A4_TECH_VID, BLOODY_W90_MAX_PID, 2, 0xFF33, 0x053D);
REGISTER_HID_DETECTOR_IPU("Bloody W90 Pro", DetectA4TechMouseControllers, A4_TECH_VID, BLOODY_W90_PRO_PID, 2, 0xFF33, 0x054D);
REGISTER_HID_DETECTOR_IPU("Bloody MP 50RS", DetectA4TechMouseControllers, A4_TECH_VID, BLOODY_MP_50RS_PID, 2, 0xFFF2, 0x6009);
REGISTER_HID_DETECTOR_IPU("Bloody B820R", DetectBloodyB820R, A4_TECH_VID, BLOODY_B820R_PID, 2, 0xFF52, 0x0210);
@@ -0,0 +1,40 @@
/*---------------------------------------------------------*\
| BloodyB820RControllerDetect.cpp |
| |
| Detector for Bloody B820R Devices |
| |
| Mohammed Julfikar Ali Mahbub (o-julfikar) 01 Apr 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "BloodyB820RController.h"
#include "DetectionManager.h"
#include "RGBController_BloodyB820R.h"
/*---------------------------------------------------------*\
| A4 Tech USB vendor ID |
\*---------------------------------------------------------*/
#define A4_TECH_VID 0x09DA
DetectedControllers DetectBloodyB820R(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
BloodyB820RController* controller = new BloodyB820RController(dev, info->path, name);
RGBController_BloodyB820R* rgb_controller = new RGBController_BloodyB820R(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Bloody B820R", DetectBloodyB820R, A4_TECH_VID, BLOODY_B820R_PID, 2, 0xFF52, 0x0210);
@@ -0,0 +1,44 @@
/*---------------------------------------------------------*\
| BloodyMouseControllerDetect.cpp |
| |
| Detector for Bloody Mouse Devices |
| |
| Chris M (Dr_No) 30 Jun 2022 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "BloodyMouseController.h"
#include "DetectionManager.h"
#include "RGBController_BloodyMouse.h"
/*---------------------------------------------------------*\
| A4 Tech USB vendor ID |
\*---------------------------------------------------------*/
#define A4_TECH_VID 0x09DA
DetectedControllers DetectBloodyMouseControllers(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
BloodyMouseController* controller = new BloodyMouseController(dev, info->path, info->product_id, name);
RGBController_BloodyMouse* rgb_controller = new RGBController_BloodyMouse(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Bloody W60 Pro", DetectBloodyMouseControllers, A4_TECH_VID, BLOODY_W60_PRO_PID, 2, 0xFF33, 0x0529);
REGISTER_HID_DETECTOR_IPU("Bloody W70 Max", DetectBloodyMouseControllers, A4_TECH_VID, BLOODY_W70_MAX_PID, 2, 0xFF33, 0x0518);
REGISTER_HID_DETECTOR_IPU("Bloody W90 Max", DetectBloodyMouseControllers, A4_TECH_VID, BLOODY_W90_MAX_PID, 2, 0xFF33, 0x053D);
REGISTER_HID_DETECTOR_IPU("Bloody W90 Pro", DetectBloodyMouseControllers, A4_TECH_VID, BLOODY_W90_PRO_PID, 2, 0xFF33, 0x054D);
REGISTER_HID_DETECTOR_IPU("Bloody MP 50RS", DetectBloodyMouseControllers, A4_TECH_VID, BLOODY_MP_50RS_PID, 2, 0xFFF2, 0x6009);
@@ -334,9 +334,14 @@ void RGBController_PolychromeUSB::DeviceUpdateLEDs()
}
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
if(zones[zone_idx].leds_count == 0)
{
continue;
}
unsigned char set_mode = zones_info[zone_idx].mode;
if (set_mode>modes.size())
if(set_mode>modes.size())
{
set_mode = active_mode;
}
@@ -347,6 +352,11 @@ void RGBController_PolychromeUSB::DeviceUpdateLEDs()
void RGBController_PolychromeUSB::DeviceUpdateZoneLEDs(int zone)
{
if(zones[zone].leds_count == 0)
{
return;
}
unsigned char set_mode=zones_info[zone].mode;
if(set_mode > modes.size())
@@ -354,7 +364,7 @@ void RGBController_PolychromeUSB::DeviceUpdateZoneLEDs(int zone)
set_mode = active_mode;
}
controller->WriteZone(zone, set_mode, zones_info[zone].speed, zones[zone].colors[0], false);
controller->WriteZone((unsigned char)zone, set_mode, zones_info[zone].speed, zones[zone].colors[0], false);
}
void RGBController_PolychromeUSB::DeviceUpdateSingleLED(int led)
@@ -362,12 +372,17 @@ void RGBController_PolychromeUSB::DeviceUpdateSingleLED(int led)
unsigned int channel = leds[led].value;
unsigned char set_mode = zones_info[channel].mode;
if(zones[channel].leds_count == 0)
{
return;
}
if(set_mode > modes.size())
{
set_mode = active_mode;
}
controller->WriteZone(channel, set_mode, zones_info[channel].speed, zones[channel].colors[0], false);
controller->WriteZone((unsigned char)channel, set_mode, zones_info[channel].speed, zones[channel].colors[0], false);
}
unsigned char RGBController_PolychromeUSB::GetDeviceMode(unsigned char zone)
@@ -1,7 +1,7 @@
/*---------------------------------------------------------*\
| AlienwareKeyboardControllerDetect.cpp |
| AlienwareAW410KControllerDetect.cpp |
| |
| Detector for Alienware Keyboard |
| Detector for Alienware AW410K Keyboard |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -10,38 +10,17 @@
#include <hidapi.h>
#include "DetectionManager.h"
#include "AlienwareAW410KController.h"
#include "AlienwareAW510KController.h"
#include "RGBController_AlienwareAW410K.h"
#include "RGBController_AlienwareAW510K.h"
/*---------------------------------------------------------*\
| Alienware vendor ID |
\*---------------------------------------------------------*/
#define ALIENWARE_VID 0x04F2
#define ALIENWARE_VID 0x04F2
/*---------------------------------------------------------*\
| Keyboard product IDs |
\*---------------------------------------------------------*/
#define ALIENWARE_AW510K_PID 0x1830
#define ALIENWARE_AW410K_PID 0x1968
DetectedControllers DetectAlienwareAW510KControllers(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
AlienwareAW510KController* controller = new AlienwareAW510KController(dev, info->path, name);
RGBController_AlienwareAW510K* rgb_controller = new RGBController_AlienwareAW510K(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
#define ALIENWARE_AW410K_PID 0x1968
DetectedControllers DetectAlienwareAW410KControllers(hid_device_info* info, const std::string& name)
{
@@ -61,5 +40,4 @@ DetectedControllers DetectAlienwareAW410KControllers(hid_device_info* info, cons
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Alienware AW510K", DetectAlienwareAW510KControllers, ALIENWARE_VID, ALIENWARE_AW510K_PID, 0x02, 0xFF00, 0x01);
REGISTER_HID_DETECTOR_IPU("Alienware AW410K", DetectAlienwareAW410KControllers, ALIENWARE_VID, ALIENWARE_AW410K_PID, 0x02, 0xFF00, 0x01);
@@ -0,0 +1,43 @@
/*---------------------------------------------------------*\
| AlienwareAW510KControllerDetect.cpp |
| |
| Detector for Alienware AW510K Keyboard |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "AlienwareAW510KController.h"
#include "RGBController_AlienwareAW510K.h"
/*---------------------------------------------------------*\
| Alienware vendor ID |
\*---------------------------------------------------------*/
#define ALIENWARE_VID 0x04F2
/*---------------------------------------------------------*\
| Keyboard product IDs |
\*---------------------------------------------------------*/
#define ALIENWARE_AW510K_PID 0x1830
DetectedControllers DetectAlienwareAW510KControllers(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
AlienwareAW510KController* controller = new AlienwareAW510KController(dev, info->path, name);
RGBController_AlienwareAW510K* rgb_controller = new RGBController_AlienwareAW510K(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Alienware AW510K", DetectAlienwareAW510KControllers, ALIENWARE_VID, ALIENWARE_AW510K_PID, 0x02, 0xFF00, 0x01);
@@ -45,7 +45,8 @@ void ColorfulGPUController::SetDirect(RGBColor color)
uint8_t g = RGBGetGValue(color);
uint8_t b = RGBGetBValue(color);
if(this->bus->info.pci_subsystem_device == COLORFUL_IGAME_RTX_4070_VULCAN_OCV)
if(this->bus->info.pci_subsystem_device == COLORFUL_IGAME_RTX_4070_VULCAN_OCV
|| this->bus->info.pci_subsystem_device == COLORFUL_IGAME_RTX_4090_NEPTUNE_OCV)
{
uint8_t data_pkt[COLORFUL_PACKET_LENGTH_V2] = { 0xAA, 0xEF, 0x01, 0x04, 0x88, 0x26 };
for(int i=6; i < COLORFUL_PACKET_LENGTH_V2 -2; i = i + 3)
@@ -1,4 +1,4 @@
/*---------------------------------------------------------*\
/*---------------------------------------------------------*\
| ColorfulGPUControllerDetect.cpp |
| |
| Detector for Colorful GPU |
@@ -74,6 +74,7 @@ REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Vulcan OC-V", Detec
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 SUPER Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070S_ULTRAW_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4080_ULTRAW_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4080_ULTRAW_OCV2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4090 Neptune OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4090_NEPTUNE_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4090 Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4090_ADVANCED_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4090 Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4090_ADVANCED_OCV2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060_ULTRAW_OC, 0x61);
@@ -81,4 +82,5 @@ REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ultra W OC", Detec
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ti Ultra W DUO OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ti Ultra W DUO OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC_2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5070_ULTRAW_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5070_ULTRAW_OCV2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5070_ULTRAW_OCV2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5070 Ti Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5070TI_ULTRAW_OC, 0x61);
@@ -526,27 +526,26 @@ void CorsairPeripheralController::ReadFirmwareInfo()
char offset = 0;
/*-----------------------------------------------------*\
| Zero out buffer |
| Some Corsair keyboards (e.g. K70 RGB MK.2) do not |
| answer the firmware-info query on the control |
| interface until several seconds after the host first |
| talks to them following a cold boot. A single read |
| attempt returns all zeros, the device type comes back |
| as 0x00, and the device is discarded as UNKNOWN. |
| Retry the query until a valid device type byte is |
| returned or the attempt cap is reached. Each failed |
| attempt is paced by the 1s hid_read_timeout below, so |
| the cap also bounds the detection stall for a device |
| that opens but never answers. Observed need |
| on a K70 RGB MK.2 cold boot is 2 attempts; the cap |
| leaves generous headroom. |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH);
int attempt = 0;
/*-----------------------------------------------------*\
| Set up Read Firmware Info packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = CORSAIR_COMMAND_READ;
usb_buf[0x02] = CORSAIR_PROPERTY_FIRMWARE_INFO;
/*-----------------------------------------------------*\
| Send packet and try reading it using an HID read |
| If that fails, repeat the send and read the reply as |
| a feature report. |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH);
actual = hid_read_timeout(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH, 1000);
if(actual == 0)
do
{
offset = 0;
/*-------------------------------------------------*\
| Zero out buffer |
\*-------------------------------------------------*/
@@ -559,10 +558,48 @@ void CorsairPeripheralController::ReadFirmwareInfo()
usb_buf[0x01] = CORSAIR_COMMAND_READ;
usb_buf[0x02] = CORSAIR_PROPERTY_FIRMWARE_INFO;
hid_send_feature_report(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH);
actual = hid_get_feature_report(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH);
offset = 1;
}
/*-------------------------------------------------*\
| Send packet and try reading it using an HID read |
| If that fails, repeat the send and read the reply |
| as a feature report. |
\*-------------------------------------------------*/
hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH);
actual = hid_read_timeout(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH, 1000);
if(actual == 0)
{
/*---------------------------------------------*\
| Zero out buffer |
\*---------------------------------------------*/
memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH);
/*---------------------------------------------*\
| Set up Read Firmware Info packet |
\*---------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = CORSAIR_COMMAND_READ;
usb_buf[0x02] = CORSAIR_PROPERTY_FIRMWARE_INFO;
hid_send_feature_report(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH);
actual = hid_get_feature_report(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH);
offset = 1;
}
attempt++;
LOG_DEBUG("[%s] Firmware info attempt %d/%d: device type %02X", CORSAIR_PERIPHERAL_CONTROLLER_NAME, attempt, CORSAIR_FW_INFO_MAX_ATTEMPTS, usb_buf[0x14 + offset]);
/*-------------------------------------------------*\
| Stop as soon as a valid device type is returned |
| 0xC0 keyboard / 0xC1 mouse / 0xC2 pad or stand |
\*-------------------------------------------------*/
if(usb_buf[0x14 + offset] == 0xC0
|| usb_buf[0x14 + offset] == 0xC1
|| usb_buf[0x14 + offset] == 0xC2)
{
break;
}
} while(attempt < CORSAIR_FW_INFO_MAX_ATTEMPTS);
/*-----------------------------------------------------*\
| Get device type |
@@ -570,7 +607,7 @@ void CorsairPeripheralController::ReadFirmwareInfo()
| 0xC1 Device is a mouse |
| 0xC2 Device is a mousepad or headset stand |
\*-----------------------------------------------------*/
LOG_DEBUG("[%s] Device type %02X", CORSAIR_PERIPHERAL_CONTROLLER_NAME, usb_buf[0x14 + offset]);
LOG_DEBUG("[%s] Device type %02X after %d attempt(s)", CORSAIR_PERIPHERAL_CONTROLLER_NAME, usb_buf[0x14 + offset], attempt);
switch(usb_buf[0x14 + offset])
{
@@ -16,6 +16,7 @@
#include "RGBController.h"
#define CORSAIR_PERIPHERAL_PACKET_LENGTH 65
#define CORSAIR_FW_INFO_MAX_ATTEMPTS 10
enum
{
@@ -0,0 +1,49 @@
/*---------------------------------------------------------*\
| EVGAKeyboardControllerDetect.cpp |
| |
| Detector for EVGA keyboard devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "RGBController_EVGAKeyboard.h"
/*---------------------------------------------------------*\
| EVGA USB vendor ID |
\*---------------------------------------------------------*/
#define EVGA_USB_VID 0x3842
/*---------------------------------------------------------*\
| Keyboard product IDs |
\*---------------------------------------------------------*/
#define Z15_ISO_PID 0x260E
#define Z15_ANSI_PID 0x2608
#define Z20_ANSI_PID 0x260A
#define Z20_UK_PID 0x2610
DetectedControllers DetectEVGAKeyboardControllers(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
EVGAKeyboardController* controller = new EVGAKeyboardController(dev, info->path, info->product_id, name);
RGBController_EVGAKeyboard* rgb_controller = new RGBController_EVGAKeyboard(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("EVGA Z15 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z15_ISO_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA Z15 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z15_ANSI_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA Z20 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z20_ANSI_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA Z20 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z20_UK_PID, 1, 0x08, 0x4B);
@@ -1,7 +1,7 @@
/*---------------------------------------------------------*\
| EVGAUSBControllerDetect.cpp |
| EVGAMouseControllerDetect.cpp |
| |
| Detector for EVGA USB devices |
| Detector for EVGA mouse devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -9,45 +9,18 @@
#include <hidapi.h>
#include "DetectionManager.h"
#include "RGBController_EVGAKeyboard.h"
#include "RGBController_EVGAMouse.h"
/*---------------------------------------------------------*\
| EVGA USB vendor ID |
\*---------------------------------------------------------*/
#define EVGA_USB_VID 0x3842
/*---------------------------------------------------------*\
| Keyboard product IDs |
\*---------------------------------------------------------*/
#define Z15_ISO_PID 0x260E
#define Z15_ANSI_PID 0x2608
#define Z20_ANSI_PID 0x260A
#define Z20_UK_PID 0x2610
#define EVGA_USB_VID 0x3842
/*---------------------------------------------------------*\
| Mouse product IDs |
\*---------------------------------------------------------*/
#define X20_WIRED_PID 0x2420
#define X20_WIRELESS_ADAPTER_PID 0x2402
DetectedControllers DetectEVGAKeyboardControllers(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
EVGAKeyboardController* controller = new EVGAKeyboardController(dev, info->path, info->product_id, name);
RGBController_EVGAKeyboard* rgb_controller = new RGBController_EVGAKeyboard(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
#define X20_WIRED_PID 0x2420
#define X20_WIRELESS_ADAPTER_PID 0x2402
DetectedControllers DetectEVGAMouse(hid_device_info* info, const std::string &name, int connection_type)
{
@@ -77,10 +50,5 @@ DetectedControllers DetectWirelessEVGAMouse(hid_device_info* info, const std::st
return(DetectEVGAMouse(info, name, EVGA_PERIPHERAL_CONNECTION_TYPE_WIRELESS));
}
REGISTER_HID_DETECTOR_IPU("EVGA Z15 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z15_ISO_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA Z15 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z15_ANSI_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA Z20 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z20_ANSI_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA Z20 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z20_UK_PID, 1, 0x08, 0x4B);
REGISTER_HID_DETECTOR_IPU("EVGA X20 Gaming Mouse", DetectWiredEVGAMouse, EVGA_USB_VID, X20_WIRED_PID, 2, 0xFFFF, 0);
REGISTER_HID_DETECTOR_IPU("EVGA X20 USB Receiver", DetectWirelessEVGAMouse, EVGA_USB_VID, X20_WIRELESS_ADAPTER_PID, 2, 0xFFFF, 0);
@@ -0,0 +1,81 @@
/*---------------------------------------------------------*\
| EVisionKeyboardControllerDetect.cpp |
| |
| Detector for EVision/Huafenda keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "EVisionKeyboardController.h"
#include "RGBController_EVisionKeyboard.h"
#include "ResourceManager.h"
#include "SettingsManager.h"
/*---------------------------------------------------------*\
| EVision vendor ID |
| NOTE: EVision (and Huafenda) use rebranded Sonix MCUs |
| Some EVision/Huafenda keyboards use the Sonix VID while |
| others use EVision's VID |
\*---------------------------------------------------------*/
#define SONIX_VID 0x0C45
#define EVISION_VID 0x320F
/*---------------------------------------------------------*\
| EVision Usage Page |
\*---------------------------------------------------------*/
#define EVISION_KEYBOARD_USAGE_PAGE 0xFF1C
/*---------------------------------------------------------*\
| Keyboard product IDs |
\*---------------------------------------------------------*/
#define EVISION_SONIX_5004_PID 0x5004
#define EVISION_SONIX_5104_PID 0x5104
#define EVISION_SONIX_5204_PID 0x5204
#define EVISION_SONIX_652F_PID 0x652F
#define EVISION_SONIX_7672_PID 0x7672
#define EVISION_SONIX_7698_PID 0x7698
#define EVISION_SONIX_8520_PID 0x8520
#define EVISION_5000_PID 0x5000
#define EVISION_502A_PID 0x502A
#define EVISION_505B_PID 0x505B
#define EVISION_5064_PID 0x5064
#define EVISION_5078_PID 0x5078
#define EVISION_5084_PID 0x5084
DetectedControllers DetectEVisionKeyboards(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
EVisionKeyboardController* controller = new EVisionKeyboardController(dev, info->path, name);
RGBController_EVisionKeyboard* rgb_controller = new RGBController_EVisionKeyboard(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
/*---------------------------------------------------------*\
| Keyboards |
\*---------------------------------------------------------*/
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:5004", DetectEVisionKeyboards, SONIX_VID, EVISION_SONIX_5004_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:5104", DetectEVisionKeyboards, SONIX_VID, EVISION_SONIX_5104_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:5204", DetectEVisionKeyboards, SONIX_VID, EVISION_SONIX_5204_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:652F", DetectEVisionKeyboards, SONIX_VID, EVISION_SONIX_652F_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:7672", DetectEVisionKeyboards, SONIX_VID, EVISION_SONIX_7672_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:7698", DetectEVisionKeyboards, SONIX_VID, EVISION_SONIX_7698_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:8520", DetectEVisionKeyboards, SONIX_VID, EVISION_SONIX_8520_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5000", DetectEVisionKeyboards, EVISION_VID, EVISION_5000_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:502A", DetectEVisionKeyboards, EVISION_VID, EVISION_502A_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:505B", DetectEVisionKeyboards, EVISION_VID, EVISION_505B_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5078", DetectEVisionKeyboards, EVISION_VID, EVISION_5078_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5064", DetectEVisionKeyboards, EVISION_VID, EVISION_5064_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5084", DetectEVisionKeyboards, EVISION_VID, EVISION_5084_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
@@ -1,129 +0,0 @@
/*---------------------------------------------------------*\
| EVisionKeyboardControllerDetect.cpp |
| |
| Detector for EVision keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "EVisionKeyboardController.h"
#include "EVisionV2KeyboardController.h"
#include "RGBController_EVisionKeyboard.h"
#include "RGBController_EVisionV2Keyboard.h"
#include "ResourceManager.h"
#include "SettingsManager.h"
/*---------------------------------------------------------*\
| Keyboard product IDs |
\*---------------------------------------------------------*/
#define EVISION_KEYBOARD_VID 0x0C45
#define EVISION_KEYBOARD2_VID 0x320F
#define EVISION_KEYBOARD3_VID 0x3299
#define EVISION_KEYBOARD_USAGE_PAGE 0xFF1C
#define ENDORFY_OMNIS_PID 0x0012
#define DEXP_BLAZE_PID 0x5084
#define GLORIOUS_GMMK_TKL_PID 0x5064
#define REDRAGON_K550_PID 0x5204
#define MARS_GAMING_MKMINI_PID 0x5078
#define SKILLKORP_K5_PID 0x505B
#define REDRAGON_K552_PID 0x5104
#define REDRAGON_K552_V2_PID 0x5000
#define REDRAGON_K556_PID 0x5004
#define TECWARE_PHANTOM_ELITE_PID 0x652F
#define WARRIOR_KANE_TC235 0x8520
#define WOMIER_K87_PID 0x502A
#define WOMIER_K66_PID 0x7698
#define BYGG_CSB_ICL01_PID 0x5041
#define GAMEPOWER_OGRE_RGB_PID 0x7672
DetectedControllers DetectEVisionKeyboards(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
EVisionKeyboardController* controller = new EVisionKeyboardController(dev, info->path, name);
RGBController_EVisionKeyboard* rgb_controller = new RGBController_EVisionKeyboard(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectEVisionV2Keyboards(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
json settings = ResourceManager::get()->GetSettingsManager()->GetSettings("EVision2Settings");
hid_device* dev = hid_open_path(info->path);
if(dev)
{
EVisionV2KeyboardController* controller = new EVisionV2KeyboardController(dev, info->path, EVISION_V2_KEYBOARD_LAYOUT, name);
RGBController_EVisionV2Keyboard* rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_KEYBOARD);
detected_controllers.push_back(rgb_controller);
if(!settings.contains("AdditionalZones") || settings["AdditionalZones"] == true)
{
rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_LOGO);
detected_controllers.push_back(rgb_controller);
rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_EDGE);
detected_controllers.push_back(rgb_controller);
}
}
return(detected_controllers);
}
DetectedControllers DetectEndorfyKeyboards(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
json settings = ResourceManager::get()->GetSettingsManager()->GetSettings("EndorfySettings");
hid_device* dev = hid_open_path(info->path);
if(dev)
{
EVisionV2KeyboardController* controller = new EVisionV2KeyboardController(dev, info->path, ENDORFY_KEYBOARD_LAYOUT, name);
RGBController_EVisionV2Keyboard* rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_KEYBOARD);
detected_controllers.push_back(rgb_controller);
if(!settings.contains("AdditionalZones") || settings["AdditionalZones"] == true)
{
rgb_controller = new RGBController_EVisionV2Keyboard(controller, ENDORFY_KEYBOARD_PART_EDGE);
detected_controllers.push_back(rgb_controller);
}
}
return(detected_controllers);
}
/*---------------------------------------------------------------------------------------------------------------------------------------------*\
| Keyboards |
\*---------------------------------------------------------------------------------------------------------------------------------------------*/
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5078", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, MARS_GAMING_MKMINI_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:5204", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, REDRAGON_K550_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:5104", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, REDRAGON_K552_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5000", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, REDRAGON_K552_V2_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:5004", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, REDRAGON_K556_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:652F", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, TECWARE_PHANTOM_ELITE_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:8520", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, WARRIOR_KANE_TC235, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:502A", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, WOMIER_K87_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:7698", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, WOMIER_K66_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5064", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, GLORIOUS_GMMK_TKL_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5084", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, DEXP_BLAZE_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:505B", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, SKILLKORP_K5_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("Endorfy Omnis", DetectEndorfyKeyboards, EVISION_KEYBOARD3_VID, ENDORFY_OMNIS_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("CSB/ICL01 Keyboard", DetectEVisionV2Keyboards, EVISION_KEYBOARD2_VID, BYGG_CSB_ICL01_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("Gamepower Ogre RGB 0C45:7672", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, GAMEPOWER_OGRE_RGB_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
@@ -0,0 +1,90 @@
/*---------------------------------------------------------*\
| EVisionKeyboardV2ControllerDetect.cpp |
| |
| Detector for EVision V2 keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "EVisionV2KeyboardController.h"
#include "RGBController_EVisionV2Keyboard.h"
#include "ResourceManager.h"
#include "SettingsManager.h"
/*---------------------------------------------------------*\
| EVision vendor ID |
\*---------------------------------------------------------*/
#define EVISION_VID 0x320F
#define SPCGEAR_VID 0x3299
/*---------------------------------------------------------*\
| EVision Usage Page |
\*---------------------------------------------------------*/
#define EVISION_KEYBOARD_USAGE_PAGE 0xFF1C
/*---------------------------------------------------------*\
| Keyboard product IDs |
\*---------------------------------------------------------*/
#define ENDORFY_OMNIS_PID 0x0012
#define BYGG_CSB_ICL01_PID 0x5041
DetectedControllers DetectEVisionV2Keyboards(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
json settings = ResourceManager::get()->GetSettingsManager()->GetSettings("EVision2Settings");
hid_device* dev = hid_open_path(info->path);
if(dev)
{
EVisionV2KeyboardController* controller = new EVisionV2KeyboardController(dev, info->path, EVISION_V2_KEYBOARD_LAYOUT, name);
RGBController_EVisionV2Keyboard* rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_KEYBOARD);
detected_controllers.push_back(rgb_controller);
if(!settings.contains("AdditionalZones") || settings["AdditionalZones"] == true)
{
rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_LOGO);
detected_controllers.push_back(rgb_controller);
rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_EDGE);
detected_controllers.push_back(rgb_controller);
}
}
return(detected_controllers);
}
DetectedControllers DetectEndorfyKeyboards(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
json settings = ResourceManager::get()->GetSettingsManager()->GetSettings("EndorfySettings");
hid_device* dev = hid_open_path(info->path);
if(dev)
{
EVisionV2KeyboardController* controller = new EVisionV2KeyboardController(dev, info->path, ENDORFY_KEYBOARD_LAYOUT, name);
RGBController_EVisionV2Keyboard* rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_KEYBOARD);
detected_controllers.push_back(rgb_controller);
if(!settings.contains("AdditionalZones") || settings["AdditionalZones"] == true)
{
rgb_controller = new RGBController_EVisionV2Keyboard(controller, ENDORFY_KEYBOARD_PART_EDGE);
detected_controllers.push_back(rgb_controller);
}
}
return(detected_controllers);
}
/*---------------------------------------------------------*\
| Keyboards |
\*---------------------------------------------------------*/
REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5041", DetectEVisionV2Keyboards, EVISION_VID, BYGG_CSB_ICL01_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("Endorfy Omnis", DetectEndorfyKeyboards, SPCGEAR_VID, ENDORFY_OMNIS_PID, 1, EVISION_KEYBOARD_USAGE_PAGE);
@@ -99,3 +99,4 @@ REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3080 Phoenix", DetectGa
REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3080 Ti Phoenix", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, GAINWARD_SUB_VEN, NVIDIA_RTX3080TI_DEV, 0x49);
REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3090 Phoenix", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, GAINWARD_SUB_VEN, NVIDIA_RTX3090_DEV, 0x49);
REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3090 Ti Phantom", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, GAINWARD_SUB_VEN, GAINWARD_RTX_3090TI_PHANTOM, 0x49);
REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 4080 Phantom GS", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, GAINWARD_SUB_VEN, GAINWARD_RTX_4080_PHANTOM_GS_SUB_DEV, 0x49);
@@ -170,6 +170,7 @@ REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 MASTER ICE",
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7600 GAMING OC 8G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI33_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7600_GAMING_OC_8G_SUB_DEV, 0x55);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7600 GAMING OC 8G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI33_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7600_GAMING_OC_8G_SUB_DEV2, 0x55);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7600 XT GAMING OC 16G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI33_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7600XT_GAMING_OC_16G_SUB_DEV, 0x55);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 6700 XT EAGLE 12G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX6700XT_EAGLE_12G_SUB_DEV, 0x63);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 6700 XT GAMING OC 12G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX6700XT_GAMING_OC_12G_SUB_DEV, 0x62);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 6800 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, GIGABYTE_SUB_VEN, GIGABYTE_RX6800XT_GAMING_OC_SUB_DEV, 0x62);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 6900 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, GIGABYTE_SUB_VEN, GIGABYTE_RX6900XT_GAMING_OC_SUB_DEV, 0x62);
File diff suppressed because it is too large Load Diff
@@ -64,7 +64,7 @@ RGBFusion2USBController::RGBFusion2USBController(hid_device* handle, const char*
{
return;
}
if(report.support_cmd_flag >= 0x02)
if(report.support_cmd_flag & 0x02)
{
EnableLampArray(false);
}
@@ -365,7 +365,18 @@ bool RGBFusion2USBController::SetStripBuiltinEffectState(int hdr, bool enable)
if(hdr == -1)
{
bitmask = 0x01 | 0x02 | 0x08 | 0x10;
switch(product_id)
{
case 0xa100:
bitmask = 0x01;
break;
case 0x5711:
bitmask = 0x01 | 0x02 | 0x08 | 0x10;
break;
default:
bitmask = 0x01 | 0x02;
break;
}
}
else
{
@@ -394,8 +405,6 @@ bool RGBFusion2USBController::SetStripBuiltinEffectState(int hdr, bool enable)
int new_effect_disabled = enable
? (base_mask & ~bitmask)
: (base_mask | bitmask);
// Skip redundant writes only after we have synchronized at least once
if(effect_disabled >= 0 && new_effect_disabled == effect_disabled)
{
return true;
@@ -440,7 +449,11 @@ bool RGBFusion2USBController::EnableLampArray(bool enable)
std::string RGBFusion2USBController::GetDeviceName()
{
return(name);
if(product_id == 0xa100)
{
return ("GC-USB");
}
return (name);
}
std::string RGBFusion2USBController::GetDeviceDescription()
@@ -736,31 +749,39 @@ std::vector<Gen2StripInfo> RGBFusion2USBController::ExportGen2Strips() const
/*---------------------------------------------------------*\
| Scan Headers for Gen2 Devices |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::ScanGen2Strips()
bool RGBFusion2USBController::ScanGen2Strips(uint8_t enabled_headers)
{
for(unsigned i = 0; i < 4; ++i)
for(unsigned int i = 0; i < 4; ++i)
{
if(g2_strip_info[i].LedsOfStrip.capacity() < 15)
{
g2_strip_info[i].LedsOfStrip.reserve(15);
}
g2_strip_info[i].numStrip = 0;
g2_strip_info[i].totalLeds = 0;
g2_strip_info[i].LedsOfStrip.resize(0);
g2_strip_info[i].numStrip = 0;
g2_strip_info[i].totalLeds = 0;
g2_strip_info[i].LedsOfStrip.clear();
}
const unsigned int hdr_lim = (product_id == 0x5711) ? 4u : 2u;
for(unsigned int slot = 0; slot < hdr_lim; ++slot)
{
if(!(enabled_headers & (1U << slot)))
{
continue;
}
static constexpr uint8_t delta[4] = {4, 5, 0, 1};
uint8_t scan_cmd = static_cast<uint8_t>(GEN2_LED_BASE_SCAN + delta[slot]);
uint8_t info_cmd = static_cast<uint8_t>(scan_cmd + 2);
uint8_t scan_cmd = GEN2_LED_BASE_SCAN + delta[slot];
uint8_t info_cmd = scan_cmd + 2;
if(!SendCCReport(scan_cmd, 0x00, 0x00))
{
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(700));
if(!SendCCReport(info_cmd, 0x00, 0x00))
@@ -768,48 +789,60 @@ bool RGBFusion2USBController::ScanGen2Strips()
return false;
}
unsigned char feature_buf[64] = {0};
feature_buf[0] = report_id;
int recv_len = hid_get_feature_report(dev, feature_buf, sizeof(feature_buf));
unsigned char feat_buf[64] = {0};
feat_buf[0] = report_id;
int recv_len = hid_get_feature_report
(
dev,
feat_buf,
sizeof(feat_buf)
);
if(recv_len < 64)
{
return false;
}
int seg_count = static_cast<int>(feature_buf[1]);
if(seg_count < 0)
{
seg_count = 0;
}
int seg_count = feat_buf[1];
if(seg_count > 15)
{
seg_count = 15;
seg_count = 15;
}
Gen2StripInfo& dst = g2_strip_info[slot];
dst.numStrip = static_cast<uint8_t>(seg_count);
dst.LedsOfStrip.resize(static_cast<size_t>(seg_count));
uint32_t total_leds = 0;
const int counts_base = 2;
Gen2StripInfo& dst = g2_strip_info[slot];
dst.numStrip = seg_count;
dst.LedsOfStrip.resize(seg_count);
uint32_t total_leds = 0;
const int counts_base = 2;
for(int k = 0; k < seg_count; ++k)
{
const int off = counts_base + (k * 2);
uint16_t lo = static_cast<uint16_t>(feature_buf[off + 0]);
uint16_t hi = static_cast<uint16_t>(feature_buf[off + 1]);
uint16_t cnt = static_cast<uint16_t>(lo | (hi << 8));
dst.LedsOfStrip[static_cast<size_t>(k)] = cnt;
total_leds += cnt;
const int off = counts_base + (k * 2);
uint16_t lo = feat_buf[off];
uint16_t hi = feat_buf[off + 1];
uint16_t cnt = lo | (hi << 8);
dst.LedsOfStrip[k] = cnt;
total_leds += cnt;
}
dst.totalLeds = total_leds;
dst.totalLeds = total_leds;
SetLedCount(0, 0, 0, 0);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
SaveLEDState(false);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
return true;
}
/*---------------------------------------------------------*\
| Check controller for persistent state support |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::SupportsSaveLEDState() const
{
return(report_loaded && (report.support_cmd_flag & 0x01));
}
@@ -234,6 +234,10 @@ union PktEffect
memset(buffer, 0, sizeof(buffer));
e.report_id = report_id;
if(pid ==0xA100)
{
led = -1;
}
if(led == -1)
{
e.zone0 = (pid == 0x5711) ? 0x07FF : 0xFF;
@@ -337,7 +341,9 @@ public:
bool SetStripBuiltinEffectState(int hdr, bool enable);
void SetStripColors(unsigned int hdr, RGBColor * colors, unsigned int num_colors, int single_led = -1);
bool SupportsGen2() const;
bool ScanGen2Strips();
bool ScanGen2Strips(uint8_t hdr_mask);
bool SaveLEDState(bool enable);
bool SupportsSaveLEDState() const;
EncodedCalibration GetCalibration(bool refresh_from_hw = false);
std::string GetDeviceName();
@@ -357,7 +363,6 @@ private:
uint32_t EncodeCalibrationBuffer(const std::string& rgb_order);
bool RefreshHardwareInfo();
void ResetController();
bool SaveLEDState(bool enable);
bool SaveCalState();
bool SendCCReport(uint8_t a, uint8_t b, uint8_t c = 0);
bool SendReport(uint8_t id, uint8_t a, uint8_t b, uint8_t c = 0);
@@ -18,6 +18,7 @@
#define DETECTOR_NAME "Gigabyte RGB Fusion 2 USB"
#define GC_USB_VID 0x0414
#define IT8297_VID 0x048D
#define IT8297_IFC 0
#define IT8297_U 0xCC
@@ -47,3 +48,4 @@ REGISTER_HID_DETECTOR_PU(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers,
REGISTER_HID_DETECTOR_PU(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x8950, IT8297_UPG, IT8297_U);
REGISTER_HID_DETECTOR_PU(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x5702, IT8297_UPG, IT8297_U);
REGISTER_HID_DETECTOR_PU(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x5711, IT8297_UPG, IT8297_U);
REGISTER_HID_DETECTOR_PU(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, GC_USB_VID, 0xa100, IT8297_UPG, IT8297_U);
@@ -46,7 +46,8 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Direct;
Direct.name = "Direct";
Direct.value = 0xFFFF;
Direct.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR;
Direct.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_HAS_PER_LED_COLOR;
Direct.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Direct.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
Direct.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -56,7 +57,9 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Static;
Static.name = "Static";
Static.value = EFFECT_STATIC;
Static.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Static.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_HAS_MODE_SPECIFIC_COLOR
| MODE_FLAG_MANUAL_SAVE;
Static.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Static.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
Static.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -69,7 +72,11 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = EFFECT_PULSE;
Breathing.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Breathing.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_HAS_SPEED
| MODE_FLAG_HAS_MODE_SPECIFIC_COLOR
| MODE_FLAG_HAS_RANDOM_COLOR
| MODE_FLAG_MANUAL_SAVE;
Breathing.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Breathing.brightness_max = 100; // Set 100 max due to controller quirks
Breathing.brightness = Breathing.brightness_max;
@@ -85,7 +92,11 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Blinking;
Blinking.name = "Flashing";
Blinking.value = EFFECT_BLINKING;
Blinking.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Blinking.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_HAS_SPEED
| MODE_FLAG_HAS_MODE_SPECIFIC_COLOR
| MODE_FLAG_HAS_RANDOM_COLOR
| MODE_FLAG_MANUAL_SAVE;
Blinking.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Blinking.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
Blinking.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -101,7 +112,9 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode ColorCycle;
ColorCycle.name = "Color Cycle";
ColorCycle.value = EFFECT_COLORCYCLE;
ColorCycle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
ColorCycle.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_HAS_SPEED
| MODE_FLAG_MANUAL_SAVE;
ColorCycle.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
ColorCycle.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
ColorCycle.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -114,7 +127,11 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Flashing;
Flashing.name = "Double Flash";
Flashing.value = EFFECT_DFLASH;
Flashing.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Flashing.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_HAS_SPEED
| MODE_FLAG_HAS_MODE_SPECIFIC_COLOR
| MODE_FLAG_HAS_RANDOM_COLOR
| MODE_FLAG_MANUAL_SAVE;
Flashing.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Flashing.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
Flashing.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -130,7 +147,10 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Wave;
Wave.name = "Wave";
Wave.value = EFFECT_WAVE;
Wave.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Wave.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_HAS_SPEED
| MODE_FLAG_REQUIRES_ENTIRE_DEVICE
| MODE_FLAG_MANUAL_SAVE;
Wave.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Wave.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
Wave.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -145,7 +165,8 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Random;
Random.name = "Random";
Random.value = EFFECT_RANDOM;
Random.flags = MODE_FLAG_HAS_BRIGHTNESS;
Random.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_MANUAL_SAVE;
Random.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Random.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
Random.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -157,7 +178,9 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Wave1;
Wave1.name = "Wave 1";
Wave1.value = EFFECT_WAVE1;
Wave1.flags = MODE_FLAG_HAS_BRIGHTNESS;
Wave1.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_REQUIRES_ENTIRE_DEVICE
| MODE_FLAG_MANUAL_SAVE;
Wave1.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Wave1.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
Wave1.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -169,7 +192,9 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Wave2;
Wave2.name = "Wave 2";
Wave2.value = EFFECT_WAVE2;
Wave2.flags = MODE_FLAG_HAS_BRIGHTNESS;
Wave2.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_REQUIRES_ENTIRE_DEVICE
| MODE_FLAG_MANUAL_SAVE;
Wave2.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Wave2.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
Wave2.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -181,7 +206,9 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Wave3;
Wave3.name = "Wave 3";
Wave3.value = EFFECT_WAVE3;
Wave3.flags = MODE_FLAG_HAS_BRIGHTNESS;
Wave3.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_REQUIRES_ENTIRE_DEVICE
| MODE_FLAG_MANUAL_SAVE;
Wave3.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Wave3.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
Wave3.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -193,7 +220,9 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
mode Wave4;
Wave4.name = "Wave 4";
Wave4.value = EFFECT_WAVE4;
Wave4.flags = MODE_FLAG_HAS_BRIGHTNESS;
Wave4.flags = MODE_FLAG_HAS_BRIGHTNESS
| MODE_FLAG_REQUIRES_ENTIRE_DEVICE
| MODE_FLAG_MANUAL_SAVE;
Wave4.brightness_min = RGBFUSION2_BRIGHTNESS_MIN;
Wave4.brightness_max = RGBFUSION2_BRIGHTNESS_MAX;
Wave4.brightness = RGBFUSION2_BRIGHTNESS_MAX;
@@ -202,6 +231,14 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
Wave4.color_mode = MODE_COLORS_NONE;
modes.push_back(Wave4);
if(!controller->SupportsSaveLEDState())
{
for(unsigned int mode_idx = 0; mode_idx < modes.size(); mode_idx++)
{
modes[mode_idx].flags &= ~MODE_FLAG_MANUAL_SAVE;
}
}
Init_Controller();
SetupZones();
}
@@ -225,7 +262,7 @@ RGBController_RGBFusion2USB::~RGBController_RGBFusion2USB()
\*---------------------------------------------------------*/
void RGBController_RGBFusion2USB::Init_Controller()
{
bool settings_changed = false;
const gb_fusion2_device* src_layout = gb_fusion2_device_list[device_index];
const std::string SectionGen2 = "Gigabyte-Gen2-ARGB";
const std::string SectionCustomBase = "CustomLayout";
@@ -240,37 +277,101 @@ void RGBController_RGBFusion2USB::Init_Controller()
\*---------------------------------------------------------*/
if(controller->SupportsGen2())
{
if(!device_settings.contains(SectionGen2))
static const char* gen2_header_keys[4] =
{
device_settings[SectionGen2]["Enabled"] = false;
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
"D_LED1",
"D_LED2",
"D_LED3",
"D_LED4"
};
const unsigned int header_count = controller->ExportGen2Strips().size();
bool default_enabled = false;
uint8_t enabled_headers = 0;
/*-----------------------------------------------------*\
| Preserve the old global setting when migrating |
\*-----------------------------------------------------*/
if(device_settings[SectionGen2].contains("Enabled"))
{
default_enabled = device_settings[SectionGen2]["Enabled"];
device_settings[SectionGen2].erase("Enabled");
settings_changed = true;
}
supports_gen2 = device_settings[SectionGen2]["Enabled"];
/*-----------------------------------------------------*\
| Create and read the per-header settings |
\*-----------------------------------------------------*/
for(unsigned int header = 0; header < header_count; ++header)
{
if(!device_settings[SectionGen2].contains(gen2_header_keys[header]))
{
device_settings[SectionGen2][gen2_header_keys[header]] = default_enabled;
settings_changed = true;
}
if(device_settings[SectionGen2][gen2_header_keys[header]])
{
enabled_headers |= 1U << header;
}
}
supports_gen2 = enabled_headers != 0;
if(supports_gen2)
{
controller->ScanGen2Strips();
controller->ScanGen2Strips(enabled_headers);
}
}
else if(device_settings.contains(SectionGen2))
{
device_settings.erase(SectionGen2);
settings_changed = true;
}
if(settings_changed)
{
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
/*---------------------------------------------------------*\
| Create the custom layout from the generic layout |
\*---------------------------------------------------------*/
switch(product_id)
if(device_num == 1)
{
case 0x8950:
src_layout = gb_fusion2_device_list[device_index + 1];
break;
case 0x5711:
src_layout = gb_fusion2_device_list[device_index + 2];
break;
default:
break;
switch(product_id)
{
break;
case 0x5711:
src_layout = gb_fusion2_device_list[device_index + 4];
break;
default:
src_layout = gb_fusion2_device_list[device_index + 1];
break;
}
}
else
{
switch(product_id)
{
case 0x8950:
src_layout = gb_fusion2_device_list[device_index + 2];
break;
case 0x5711:
src_layout = gb_fusion2_device_list[device_index + 3];
break;
case 0xa100:
src_layout = gb_fusion2_device_list[device_index + 5];
break;
default:
break;
}
}
if(!device_settings.contains(SectionCustom))
if(!device_settings.contains(SectionCustom) && (product_id != 0xA100))
{
device_settings[SectionCustom]["Enabled"] = false;
device_settings[SectionCustom]["Data"] = BuildCustomLayoutJson(src_layout, ReverseLedLookup);
@@ -282,7 +383,7 @@ void RGBController_RGBFusion2USB::Init_Controller()
EncodedCalibration hw_cal = controller->GetCalibration(false);
if(device_num == 0)
if(device_num == 0 || product_id != 0xa100)
{
if(!device_settings.contains(SectionCalibration))
{
@@ -382,7 +483,7 @@ void RGBController_RGBFusion2USB::Init_Controller()
(*instance_layout.zones)[zi] = (*src_layout->zones)[zi];
}
if(custom_layout)
if(custom_layout && (product_id != 0xA100))
{
LoadCustomLayoutFromJson(device_settings[SectionCustom]["Data"], LedLookup, &instance_layout);
}
@@ -428,6 +529,7 @@ void RGBController_RGBFusion2USB::SetupZones()
first_run = true;
}
/*-----------------------------------------------------*\
| Clear any existing color/LED configuration |
\*-----------------------------------------------------*/
@@ -441,7 +543,7 @@ void RGBController_RGBFusion2USB::SetupZones()
for(num_zones = 0; num_zones < GB_FUSION2_ZONES_MAX; num_zones++)
{
if(!gb_fusion2_device_list[device_index]->zones[0][num_zones])
if(!(*instance_layout.zones)[num_zones])
{
break;
}
@@ -452,13 +554,20 @@ void RGBController_RGBFusion2USB::SetupZones()
unsigned int d1 = 0, d2 = 0, d3 = 0, d4 = 0;
/*-----------------------------------------------------*\
| Set up zones (Fixed so as to not spam the controller) |
| Retrieve the latest Gen2 scan results |
\*-----------------------------------------------------*/
std::vector<Gen2StripInfo> strips;
if(supports_gen2)
{
strips = controller->ExportGen2Strips();
}
/*-----------------------------------------------------*\
| Set up zones |
\*-----------------------------------------------------*/
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
/*-------------------------------------------------*\
| Get zone configuration from device data |
\*-------------------------------------------------*/
const gb_fusion2_zone* zone_at_idx = (*instance_layout.zones)[zone_idx];
if(!zone_at_idx)
@@ -466,36 +575,241 @@ void RGBController_RGBFusion2USB::SetupZones()
continue;
}
/*-------------------------------------------------*\
| Check if this is a fixed-size zone |
\*-------------------------------------------------*/
bool fixed_zone = (zone_at_idx->leds_min == zone_at_idx->leds_max);
const Gen2StripInfo* gen2_info = nullptr;
/*-------------------------------------------------*\
| (Re-)initialize the zone |
| Locate the Gen2 result corresponding to this zone |
\*-------------------------------------------------*/
if(fixed_zone)
if(supports_gen2 && !fixed_zone)
{
unsigned int slot = 0;
switch(zone_at_idx->idx)
{
case LED4:
case HDR_D_LED2:
slot = 1;
break;
case HDR_D_LED3:
slot = 2;
break;
case HDR_D_LED4:
slot = 3;
break;
default:
slot = 0;
break;
}
if(slot < strips.size())
{
if(strips[slot].totalLeds > 0)
{
gen2_info = &strips[slot];
}
}
}
/*-------------------------------------------------*\
| Automatically detected Gen2 ARGB zone |
\*-------------------------------------------------*/
if(gen2_info != nullptr)
{
bool preserve_segments = false;
/*---------------------------------------------*\
| A saved segment configuration may only rename |
| or subdivide each detected physical strip. |
\*---------------------------------------------*/
if(zones[zone_idx].flags & ZONE_FLAG_MANUALLY_CONFIGURED_SEGMENTS)
{
bool valid_segments = true;
std::size_t segment_idx = 0;
for(std::size_t strip_idx = 0; strip_idx < gen2_info->LedsOfStrip.size(); strip_idx++)
{
unsigned int strip_leds = gen2_info->LedsOfStrip[strip_idx];
/*-------------------------------------*\
| Require one group for this strip |
\*-------------------------------------*/
if(segment_idx >= zones[zone_idx].segments.size())
{
valid_segments = false;
break;
}
if(!(zones[zone_idx].segments[segment_idx].flags & SEGMENT_FLAG_GROUP_START))
{
valid_segments = false;
break;
}
segment_idx++;
/*-------------------------------------*\
| Validate subdivisions of this strip |
\*-------------------------------------*/
unsigned int member_leds = 0;
bool found_member = false;
while(segment_idx < zones[zone_idx].segments.size())
{
const segment& member = zones[zone_idx].segments[segment_idx];
if(member.flags & SEGMENT_FLAG_GROUP_START)
{
break;
}
if(!(member.flags & SEGMENT_FLAG_GROUP_MEMBER)
|| member.type != ZONE_TYPE_LINEAR
|| member.leds_count == 0)
{
valid_segments = false;
break;
}
member_leds += member.leds_count;
if(member_leds > strip_leds)
{
valid_segments = false;
break;
}
found_member = true;
segment_idx++;
}
if(!valid_segments)
{
break;
}
if(!found_member || member_leds != strip_leds)
{
valid_segments = false;
break;
}
}
if(segment_idx != zones[zone_idx].segments.size())
{
valid_segments = false;
}
preserve_segments = valid_segments;
}
/*---------------------------------------------*\
| Gen2 scan controls all zone-level geometry |
\*---------------------------------------------*/
zones[zone_idx].name = zone_at_idx->name;
zones[zone_idx].type = ZONE_TYPE_SEGMENTED;
zones[zone_idx].leds_count = gen2_info->totalLeds;
zones[zone_idx].leds_min = zones[zone_idx].leds_count;
zones[zone_idx].leds_max = zones[zone_idx].leds_count;
zones[zone_idx].matrix_map.width = 0;
zones[zone_idx].matrix_map.height = 0;
zones[zone_idx].matrix_map.map.clear();
zones[zone_idx].flags = ZONE_FLAG_MANUALLY_CONFIGURABLE_SEGMENTS;
if(preserve_segments)
{
zones[zone_idx].flags |= ZONE_FLAG_MANUALLY_CONFIGURED_SEGMENTS;
}
else
{
zones[zone_idx].segments.clear();
zones[zone_idx].segments.reserve(gen2_info->LedsOfStrip.size() * 2);
unsigned int start_idx = 0;
for(std::size_t strip_idx = 0; strip_idx < gen2_info->LedsOfStrip.size(); strip_idx++)
{
unsigned int strip_leds = gen2_info->LedsOfStrip[strip_idx];
/*-------------------------------------*\
| Empty marker for a physical strip |
\*-------------------------------------*/
segment group;
group.name = "Strip " + std::to_string(strip_idx + 1);
group.type = ZONE_TYPE_LINEAR;
group.start_idx = 0;
group.leds_count = 0;
group.flags = SEGMENT_FLAG_GROUP_START;
group.matrix_map.width = 0;
group.matrix_map.height = 0;
group.matrix_map.map.clear();
zones[zone_idx].segments.push_back(group);
/*-------------------------------------*\
| Initial member covers the whole strip |
\*-------------------------------------*/
segment member;
member.name = "Strip " + std::to_string(strip_idx + 1);
member.type = ZONE_TYPE_LINEAR;
member.start_idx = start_idx;
member.leds_count = strip_leds;
member.flags = SEGMENT_FLAG_GROUP_MEMBER;
member.matrix_map.width = 0;
member.matrix_map.height = 0;
member.matrix_map.map.clear();
zones[zone_idx].segments.push_back(member);
start_idx += strip_leds;
}
}
}
/*-------------------------------------------------*\
| Fixed motherboard zone |
\*-------------------------------------------------*/
else if(fixed_zone)
{
zones[zone_idx].name = zone_at_idx->name;
zones[zone_idx].type = ZONE_TYPE_SINGLE;
zones[zone_idx].leds_min = zone_at_idx->leds_min;
zones[zone_idx].leds_max = zone_at_idx->leds_max;
zones[zone_idx].leds_count = zones[zone_idx].leds_min;
zones[zone_idx].leds_count = zone_at_idx->leds_min;
zones[zone_idx].flags = 0;
zones[zone_idx].matrix_map.width = 0;
zones[zone_idx].matrix_map.height = 0;
zones[zone_idx].matrix_map.map.clear();
zones[zone_idx].segments.clear();
}
/*-------------------------------------------------*\
| Manually configurable ARGB zone |
\*-------------------------------------------------*/
else
{
zones[zone_idx].leds_min = zone_at_idx->leds_min;
zones[zone_idx].leds_max = zone_at_idx->leds_max;
bool reset_manual_zone = first_run || !(zones[zone_idx].flags & ZONE_FLAG_MANUALLY_CONFIGURABLE_SIZE);
if(first_run)
if(reset_manual_zone)
{
zones[zone_idx].flags = ZONE_FLAG_MANUALLY_CONFIGURABLE_SIZE
| ZONE_FLAG_MANUALLY_CONFIGURABLE_NAME
| ZONE_FLAG_MANUALLY_CONFIGURABLE_TYPE
| ZONE_FLAG_MANUALLY_CONFIGURABLE_MATRIX_MAP
| ZONE_FLAG_MANUALLY_CONFIGURABLE_SEGMENTS;
zones[zone_idx].name = zone_at_idx->name;
zones[zone_idx].type = ZONE_TYPE_LINEAR;
zones[zone_idx].leds_count = zone_at_idx->leds_min;
zones[zone_idx].matrix_map.width = 0;
zones[zone_idx].matrix_map.height = 0;
zones[zone_idx].matrix_map.map.clear();
zones[zone_idx].segments.clear();
}
zones[zone_idx].leds_min = zone_at_idx->leds_min;
zones[zone_idx].leds_max = zone_at_idx->leds_max;
if(!(zones[zone_idx].flags & ZONE_FLAG_MANUALLY_CONFIGURED_NAME))
{
zones[zone_idx].name = zone_at_idx->name;
@@ -515,8 +829,29 @@ void RGBController_RGBFusion2USB::SetupZones()
{
zones[zone_idx].matrix_map.width = 0;
zones[zone_idx].matrix_map.height = 0;
zones[zone_idx].matrix_map.map.resize(0);
zones[zone_idx].matrix_map.map.clear();
}
if(!(zones[zone_idx].flags & ZONE_FLAG_MANUALLY_CONFIGURED_SEGMENTS))
{
zones[zone_idx].segments.clear();
}
}
/*-------------------------------------------------*\
| Initialize per-zone modes |
\*-------------------------------------------------*/
if(zones[zone_idx].modes.empty())
{
for(std::size_t mode_idx = 0; mode_idx < modes.size(); mode_idx++)
{
if(!(modes[mode_idx].flags & MODE_FLAG_REQUIRES_ENTIRE_DEVICE))
{
zones[zone_idx].modes.push_back(modes[mode_idx]);
}
}
zones[zone_idx].active_mode = -1;
}
/*-------------------------------------------------*\
@@ -525,8 +860,7 @@ void RGBController_RGBFusion2USB::SetupZones()
for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++)
{
led new_led;
new_led.name = zone_at_idx->name;
new_led.name = zones[zone_idx].name;
new_led.value = zone_at_idx->idx;
if(!fixed_zone)
@@ -538,7 +872,7 @@ void RGBController_RGBFusion2USB::SetupZones()
}
/*-------------------------------------------------*\
| If not a fixed-size zone, set up LED sizes |
| Restore the ARGB header LED counts |
\*-------------------------------------------------*/
if(!fixed_zone)
{
@@ -576,50 +910,67 @@ void RGBController_RGBFusion2USB::DeviceConfigureZone(int zone_idx)
void RGBController_RGBFusion2USB::DeviceUpdateLEDs()
{
int mode_value = (modes[active_mode].value);
bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM);
int mode_value = modes[active_mode].value;
bool random = modes[active_mode].color_mode == MODE_COLORS_RANDOM;
uint32_t* color = &null_color;
/*---------------------------------------------------------*\
| If Wave 1-4 then use special sequence. |
| Entire-device modes use the special wave sequence |
\*---------------------------------------------------------*/
if(mode_value == 6 || (mode_value >= 9 && mode_value <= 12))
if(modes[active_mode].flags & MODE_FLAG_REQUIRES_ENTIRE_DEVICE)
{
controller->SetStripBuiltinEffectState(-1, true);
controller->SetLEDEffect(-1, 1, 0, 0xFF, 0, color);
controller->SetLEDEffect(-1, EFFECT_STATIC, 0, 0xFF, false, color);
controller->ApplyEffect();
controller->SetLEDEffect( 2, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color);
controller->SetLEDEffect(2, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color);
controller->ApplyEffect();
return;
}
for(int zone_idx = 0; zone_idx < (int)zones.size(); zone_idx++)
{
mode* selected_mode = &modes[active_mode];
/*---------------------------------------------------------*\
| Use the zone-specific mode when one is selected |
\*---------------------------------------------------------*/
if(zones[zone_idx].active_mode >= 0)
{
selected_mode = &zones[zone_idx].modes[zones[zone_idx].active_mode];
}
mode_value = selected_mode->value;
random = selected_mode->color_mode == MODE_COLORS_RANDOM;
color = &null_color;
if(zones[zone_idx].type == ZONE_TYPE_SINGLE)
{
for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++)
{
int led_mode_value = mode_value;
uint32_t* led_color = color;
/*---------------------------------------------------------*\
| Motherboard LEDs always use effect mode, so use static for|
| direct mode but get colors from zone |
\*---------------------------------------------------------*/
if(modes[active_mode].value == 0xFFFF)
if(selected_mode->value == 0xFFFF)
{
color = &zones[zone_idx].colors[led_idx];
mode_value = EFFECT_STATIC;
led_color = &zones[zone_idx].colors[led_idx];
led_mode_value = EFFECT_STATIC;
}
/*---------------------------------------------------------*\
| If the mode uses mode-specific color, get color from mode |
\*---------------------------------------------------------*/
else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
else if(selected_mode->color_mode == MODE_COLORS_MODE_SPECIFIC)
{
color = &modes[active_mode].colors[0];
led_color = &selected_mode->colors[0];
}
/*---------------------------------------------------------*\
| Apply the mode and color to the zone |
\*---------------------------------------------------------*/
controller->SetLEDEffect(zones[zone_idx].leds[led_idx].value, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color);
controller->SetLEDEffect(zones[zone_idx].leds[led_idx].value, led_mode_value, selected_mode->speed, selected_mode->brightness, random, led_color);
}
}
/*---------------------------------------------------------*\
@@ -634,7 +985,7 @@ void RGBController_RGBFusion2USB::DeviceUpdateLEDs()
/*---------------------------------------------------------*\
| Direct mode addresses a different register |
\*---------------------------------------------------------*/
if(modes[active_mode].value == 0xFFFF)
if(selected_mode->value == 0xFFFF)
{
controller->SetStripBuiltinEffectState(hdr, false);
controller->SetStripColors(hdr, zones[zone_idx].colors, zones[zone_idx].leds_count);
@@ -648,45 +999,52 @@ void RGBController_RGBFusion2USB::DeviceUpdateLEDs()
/*---------------------------------------------------------*\
| If mode has mode specific color, load color from mode |
\*---------------------------------------------------------*/
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
if(selected_mode->color_mode == MODE_COLORS_MODE_SPECIFIC)
{
color = &modes[active_mode].colors[0];
color = &selected_mode->colors[0];
}
/*---------------------------------------------------------*\
| Apply hardware effects to LED strips |
\*---------------------------------------------------------*/
controller->SetStripBuiltinEffectState(hdr, true);
controller->SetLEDEffect(hdr, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color);
controller->SetLEDEffect(hdr, mode_value, selected_mode->speed, selected_mode->brightness, random, color);
}
}
}
}
controller->ApplyEffect();
}
void RGBController_RGBFusion2USB::DeviceUpdateZoneLEDs(int zone)
{
/*---------------------------------------------------------*\
| Get mode parameters |
\*---------------------------------------------------------*/
int mode_value = (modes[active_mode].value);
bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM);
int mode_value = modes[active_mode].value;
bool random = modes[active_mode].color_mode == MODE_COLORS_RANDOM;
uint32_t* color = &null_color;
/*---------------------------------------------------------*\
| If Wave 1-4 then use special sequence. |
| Entire-device modes cannot be updated one zone at a time |
\*---------------------------------------------------------*/
if(mode_value == 6 || (mode_value >= 9 && mode_value <= 12))
if(modes[active_mode].flags & MODE_FLAG_REQUIRES_ENTIRE_DEVICE)
{
controller->SetStripBuiltinEffectState(-1, true);
controller->SetLEDEffect(-1, 1, 0, 0xFF, 0, color);
controller->ApplyEffect();
controller->SetLEDEffect( 2, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color);
controller->ApplyEffect();
DeviceUpdateLEDs();
return;
}
mode* selected_mode = &modes[active_mode];
/*---------------------------------------------------------*\
| Use the zone-specific mode when one is selected |
\*---------------------------------------------------------*/
if(zones[zone].active_mode >= 0)
{
selected_mode = &zones[zone].modes[zones[zone].active_mode];
}
mode_value = selected_mode->value;
random = selected_mode->color_mode == MODE_COLORS_RANDOM;
/*---------------------------------------------------------*\
| Set motherboard LEDs |
\*---------------------------------------------------------*/
@@ -694,28 +1052,31 @@ void RGBController_RGBFusion2USB::DeviceUpdateZoneLEDs(int zone)
{
for(std::size_t led_idx = 0; led_idx < zones[zone].leds_count; led_idx++)
{
int led_mode_value = mode_value;
uint32_t* led_color = color;
/*------------------------------------------------------------*\
| Motherboard LEDs always use effect mode, so use static for |
| direct mode but get colors from zone |
\*------------------------------------------------------------*/
if(mode_value == 0xFFFF)
if(selected_mode->value == 0xFFFF)
{
color = &zones[zone].colors[led_idx];
mode_value = EFFECT_STATIC;
led_color = &zones[zone].colors[led_idx];
led_mode_value = EFFECT_STATIC;
}
/*---------------------------------------------------------*\
| If the mode uses mode-specific color, get color from mode |
\*---------------------------------------------------------*/
else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
else if(selected_mode->color_mode == MODE_COLORS_MODE_SPECIFIC)
{
color = &modes[active_mode].colors[0];
led_color = &selected_mode->colors[0];
}
/*---------------------------------------------------------*\
| Apply the mode and color to the zone |
\*---------------------------------------------------------*/
controller->SetLEDEffect(zones[zone].leds[led_idx].value, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color);
controller->SetLEDEffect(zones[zone].leds[led_idx].value, led_mode_value, selected_mode->speed, selected_mode->brightness, random, led_color);
controller->ApplyEffect();
}
}
@@ -732,7 +1093,7 @@ void RGBController_RGBFusion2USB::DeviceUpdateZoneLEDs(int zone)
/*---------------------------------------------------------*\
| Direct mode addresses a different register |
\*---------------------------------------------------------*/
if(mode_value == 0xFFFF)
if(selected_mode->value == 0xFFFF)
{
controller->SetStripBuiltinEffectState(hdr, false);
controller->SetStripColors(hdr, zones[zone].colors, zones[zone].leds_count);
@@ -746,16 +1107,16 @@ void RGBController_RGBFusion2USB::DeviceUpdateZoneLEDs(int zone)
/*---------------------------------------------------------*\
| If mode has mode specific color, load color from mode |
\*---------------------------------------------------------*/
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
if(selected_mode->color_mode == MODE_COLORS_MODE_SPECIFIC)
{
color = &modes[active_mode].colors[0];
color = &selected_mode->colors[0];
}
/*---------------------------------------------------------*\
| Apply built-in effects to LED strips |
\*---------------------------------------------------------*/
controller->SetStripBuiltinEffectState(hdr, true);
controller->SetLEDEffect(hdr, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color);
controller->SetLEDEffect(hdr, mode_value, selected_mode->speed, selected_mode->brightness, random, color);
controller->ApplyEffect();
}
}
@@ -764,26 +1125,38 @@ void RGBController_RGBFusion2USB::DeviceUpdateZoneLEDs(int zone)
void RGBController_RGBFusion2USB::DeviceUpdateSingleLED(int led)
{
/*---------------------------------------------------------*\
| Get mode parameters |
\*---------------------------------------------------------*/
int mode_value = (modes[active_mode].value);
bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM);
int mode_value = modes[active_mode].value;
bool random = modes[active_mode].color_mode == MODE_COLORS_RANDOM;
uint32_t* color = &null_color;
/*---------------------------------------------------------*\
| If Wave 1-4 then use special sequence. |
| Entire-device modes cannot update an individual LED |
\*---------------------------------------------------------*/
if(mode_value == 6 || (mode_value >= 9 && mode_value <= 12))
if(modes[active_mode].flags & MODE_FLAG_REQUIRES_ENTIRE_DEVICE)
{
controller->SetStripBuiltinEffectState(-1, true);
controller->SetLEDEffect(-1, 1, 0, 0xFF, 0, color);
controller->ApplyEffect();
controller->SetLEDEffect( 2, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color);
controller->ApplyEffect();
DeviceUpdateLEDs();
return;
}
unsigned int zone_idx = GetLED_Zone(led);
int zone_idx = GetLED_Zone(led);
if(zone_idx < 0)
{
return;
}
mode* selected_mode = &modes[active_mode];
/*---------------------------------------------------------*\
| Use the zone-specific mode when one is selected |
\*---------------------------------------------------------*/
if(zones[zone_idx].active_mode >= 0)
{
selected_mode = &zones[zone_idx].modes[zones[zone_idx].active_mode];
}
mode_value = selected_mode->value;
random = selected_mode->color_mode == MODE_COLORS_RANDOM;
/*---------------------------------------------------------*\
| Set motherboard LEDs |
@@ -794,7 +1167,7 @@ void RGBController_RGBFusion2USB::DeviceUpdateSingleLED(int led)
| Motherboard LEDs always use effect mode, so use static for|
| direct mode but get colors from zone |
\*---------------------------------------------------------*/
if(mode_value == 0xFFFF)
if(selected_mode->value == 0xFFFF)
{
color = &colors[led];
mode_value = EFFECT_STATIC;
@@ -803,12 +1176,12 @@ void RGBController_RGBFusion2USB::DeviceUpdateSingleLED(int led)
/*---------------------------------------------------------*\
| If the mode uses mode-specific color, get color from mode |
\*---------------------------------------------------------*/
else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
else if(selected_mode->color_mode == MODE_COLORS_MODE_SPECIFIC)
{
color = &modes[active_mode].colors[0];
color = &selected_mode->colors[0];
}
controller->SetLEDEffect(leds[led].value, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color);
controller->SetLEDEffect(leds[led].value, mode_value, selected_mode->speed, selected_mode->brightness, random, color);
controller->ApplyEffect();
}
@@ -826,6 +1199,11 @@ void RGBController_RGBFusion2USB::DeviceUpdateMode()
DeviceUpdateLEDs();
}
void RGBController_RGBFusion2USB::DeviceUpdateZoneMode(int zone)
{
DeviceUpdateZoneLEDs(zone);
}
int RGBController_RGBFusion2USB::GetLED_Zone(int led_idx)
{
for(int zone_idx = 0; zone_idx < (int)zones.size(); zone_idx++)
@@ -845,6 +1223,11 @@ int RGBController_RGBFusion2USB::GetLED_Zone(int led_idx)
return(-1);
}
void RGBController_RGBFusion2USB::DeviceSaveMode()
{
controller->SaveLEDState(true);
}
/*---------------------------------------------------------*\
| Convert calibration data to JSON |
\*---------------------------------------------------------*/
@@ -57,6 +57,8 @@ public:
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceUpdateZoneMode(int zone);
void DeviceSaveMode() override;
private:
std::string detector_name;
@@ -0,0 +1,43 @@
/*---------------------------------------------------------*\
| HoltekA070ControllerDetect.cpp |
| |
| Detector for Holtek A070 Mouse |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HoltekA070Controller.h"
#include "RGBController_HoltekA070.h"
/*---------------------------------------------------------*\
| Holtek Semiconductor Inc. vendor ID |
\*---------------------------------------------------------*/
#define HOLTEK_VID 0x04D9
/*---------------------------------------------------------*\
| Mouse product IDs |
\*---------------------------------------------------------*/
#define HOLTEK_A070_PID 0xA070
DetectedControllers DetectHoltekA070Controllers(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HoltekA070Controller* controller = new HoltekA070Controller(dev, info->path, name);
RGBController_HoltekA070* rgb_controller = new RGBController_HoltekA070(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Holtek USB Gaming Mouse", DetectHoltekA070Controllers, HOLTEK_VID, HOLTEK_A070_PID, 1, 0xFF00, 2);
@@ -0,0 +1,43 @@
/*---------------------------------------------------------*\
| HoltekA1FAControllerDetect.cpp |
| |
| Detector for Holtek A1FA Mousemat |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HoltekA1FAController.h"
#include "RGBController_HoltekA1FA.h"
/*---------------------------------------------------------*\
| Holtek Semiconductor Inc. vendor ID |
\*---------------------------------------------------------*/
#define HOLTEK_VID 0x04D9
/*---------------------------------------------------------*\
| Mousemats product IDs |
\*---------------------------------------------------------*/
#define HOLTEK_A1FA_PID 0xA1FA
DetectedControllers DetectHoltekA1FAControllers(hid_device_info *info, const std::string &name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HoltekA1FAController* controller = new HoltekA1FAController(dev, info->path, name);
RGBController_HoltekA1FA* rgb_controller = new RGBController_HoltekA1FA(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Holtek Mousemat", DetectHoltekA1FAControllers, HOLTEK_VID, HOLTEK_A1FA_PID, 2, 0xFF00, 0xFF00);
@@ -1,67 +0,0 @@
/*---------------------------------------------------------*\
| HoltekControllerDetect.cpp |
| |
| Detector for Holtek devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HoltekA070Controller.h"
#include "RGBController_HoltekA070.h"
#include "HoltekA1FAController.h"
#include "RGBController_HoltekA1FA.h"
/*---------------------------------------------------------*\
| Holtek Semiconductor Inc. vendor ID |
\*---------------------------------------------------------*/
#define HOLTEK_VID 0x04D9
/*---------------------------------------------------------*\
| Mouse product IDs |
\*---------------------------------------------------------*/
#define HOLTEK_A070_PID 0xA070
/*---------------------------------------------------------*\
| Mousemats product IDs |
\*---------------------------------------------------------*/
#define HOLTEK_A1FA_PID 0xA1FA
DetectedControllers DetectHoltekControllers(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HoltekA070Controller* controller = new HoltekA070Controller(dev, info->path, name);
RGBController_HoltekA070* rgb_controller = new RGBController_HoltekA070(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectHoltekMousemats(hid_device_info *info, const std::string &name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HoltekA1FAController* controller = new HoltekA1FAController(dev, info->path, name);
RGBController_HoltekA1FA* rgb_controller = new RGBController_HoltekA1FA(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Holtek USB Gaming Mouse", DetectHoltekControllers, HOLTEK_VID, HOLTEK_A070_PID, 1, 0xFF00, 2);
REGISTER_HID_DETECTOR_IPU("Holtek Mousemat", DetectHoltekMousemats, HOLTEK_VID, HOLTEK_A1FA_PID, 2, 0xFF00, 0xFF00);
@@ -0,0 +1,51 @@
/*---------------------------------------------------------*\
| HyperXAlloyElite2ControllerDetect.cpp |
| |
| Driver for HyperX Alloy Elite 2 keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HyperXAlloyElite2Controller.h"
#include "RGBController_HyperXAlloyElite2.h"
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs |
\*---------------------------------------------------------*/
#define HYPERX_KEYBOARD_VID 0x0951
#define HYPERX_ALLOY_ELITE_2_PID 0x1711
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs (HP) |
\*---------------------------------------------------------*/
#define HP_KEYBOARD_VID 0x03F0
#define HYPERX_ALLOY_ELITE_2_HP_PID 0x058F
DetectedControllers DetectHyperXAlloyElite2(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyElite2Controller* controller = new HyperXAlloyElite2Controller(dev, info->path, name);
RGBController_HyperXAlloyElite2* rgb_controller = new RGBController_HyperXAlloyElite2(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
#ifdef _WIN32
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite 2", DetectHyperXAlloyElite2, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_PID, 3, 0xFF90);
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite 2 (HP)", DetectHyperXAlloyElite2, HP_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_HP_PID, 3, 0xFF90);
#else
REGISTER_HID_DETECTOR_I("HyperX Alloy Elite 2", DetectHyperXAlloyElite2, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Elite 2 (HP)", DetectHyperXAlloyElite2, HP_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_HP_PID, 0);
#endif
@@ -0,0 +1,39 @@
/*---------------------------------------------------------*\
| HyperXAlloyEliteControllerDetect.cpp |
| |
| Driver for HyperX Alloy Elite keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HyperXAlloyEliteController.h"
#include "RGBController_HyperXAlloyElite.h"
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs |
\*---------------------------------------------------------*/
#define HYPERX_KEYBOARD_VID 0x0951
#define HYPERX_ALLOY_ELITE_PID 0x16BE
DetectedControllers DetectHyperXAlloyElite(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyEliteController* controller = new HyperXAlloyEliteController(dev, info->path, name);
RGBController_HyperXAlloyElite* rgb_controller = new RGBController_HyperXAlloyElite(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite RGB", DetectHyperXAlloyElite, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_PID, 2, 0xFF01);
@@ -0,0 +1,39 @@
/*---------------------------------------------------------*\
| HyperXAlloyFPSControllerDetect.cpp |
| |
| Driver for HyperX Alloy FPS keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HyperXAlloyFPSController.h"
#include "RGBController_HyperXAlloyFPS.h"
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs |
\*---------------------------------------------------------*/
#define HYPERX_KEYBOARD_VID 0x0951
#define HYPERX_ALLOY_FPS_RGB_PID 0x16DC
DetectedControllers DetectHyperXAlloyFPS(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyFPSController* controller = new HyperXAlloyFPSController(dev, info->path, name);
RGBController_HyperXAlloyFPS* rgb_controller = new RGBController_HyperXAlloyFPS(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IP("HyperX Alloy FPS RGB", DetectHyperXAlloyFPS, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_FPS_RGB_PID, 2, 0xFF01);
@@ -0,0 +1,71 @@
/*---------------------------------------------------------*\
| HyperXAlloyOrigins60and65ControllerDetect.cpp |
| |
| Driver for HyperX Alloy Origins 60 and 65 keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HyperXAlloyOrigins60and65Controller.h"
#include "RGBController_HyperXAlloyOrigins60and65.h"
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs |
\*---------------------------------------------------------*/
#define HYPERX_KEYBOARD_VID 0x0951
#define HYPERX_ALLOY_ORIGINS_60_PID 0x1734
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs (HP) |
\*---------------------------------------------------------*/
#define HP_KEYBOARD_VID 0x03F0
#define HYPERX_ALLOY_ORIGINS_60_HP_PID 0x0C8E
#define HYPERX_ALLOY_ORIGINS_65_HP_PID 0x038F
AlloyOrigins60and65MappingLayoutType GetAlloyOrigins60and65MappingLayoutType(int pid)
{
switch(pid)
{
case HYPERX_ALLOY_ORIGINS_60_PID:
case HYPERX_ALLOY_ORIGINS_60_HP_PID:
return ALLOY_ORIGINS_60_LAYOUT;
case HYPERX_ALLOY_ORIGINS_65_HP_PID:
return ALLOY_ORIGINS_65_LAYOUT;
default:
return ALLOY_ORIGINS_60_LAYOUT;
}
}
DetectedControllers DetectHyperXAlloyOrigins60and65(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyOrigins60and65Controller* controller = new HyperXAlloyOrigins60and65Controller(dev, info->path, name);
AlloyOrigins60and65MappingLayoutType layout = GetAlloyOrigins60and65MappingLayoutType(info->product_id);
RGBController_HyperXAlloyOrigins60and65* rgb_controller = new RGBController_HyperXAlloyOrigins60and65(controller, layout);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
#ifdef _WIN32
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60", DetectHyperXAlloyOrigins60and65, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_PID, 3);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_HP_PID, 3);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 65 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_65_HP_PID, 3);
#else
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60", DetectHyperXAlloyOrigins60and65, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_HP_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 65 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_65_HP_PID, 0);
#endif
@@ -0,0 +1,51 @@
/*---------------------------------------------------------*\
| HyperXAlloyOriginsControllerDetect.cpp |
| |
| Driver for HyperX Alloy Origins keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HyperXAlloyOriginsController.h"
#include "RGBController_HyperXAlloyOrigins.h"
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs |
\*---------------------------------------------------------*/
#define HYPERX_KEYBOARD_VID 0x0951
#define HYPERX_ALLOY_ORIGINS_PID 0x16E5
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs (HP) |
\*---------------------------------------------------------*/
#define HP_KEYBOARD_VID 0x03F0
#define HYPERX_ALLOY_ORIGINS_HP_PID 0x0591
DetectedControllers DetectHyperXAlloyOrigins(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyOriginsController* controller = new HyperXAlloyOriginsController(dev, info->path, name);
RGBController_HyperXAlloyOrigins* rgb_controller = new RGBController_HyperXAlloyOrigins(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
#ifdef _WIN32
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 3);
REGISTER_HID_DETECTOR_IP("HyperX Alloy Origins (HP)", DetectHyperXAlloyOrigins, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_HP_PID, 3, 0xFF90);
#else
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins (HP)", DetectHyperXAlloyOrigins, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_HP_PID, 0);
#endif
@@ -0,0 +1,46 @@
/*---------------------------------------------------------*\
| HyperXAlloyOriginsCoreControllerDetect.cpp |
| |
| Driver for HyperX Alloy Origins Core keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HyperXAlloyOriginsCoreController.h"
#include "RGBController_HyperXAlloyOriginsCore.h"
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs |
\*---------------------------------------------------------*/
#define HYPERX_KEYBOARD_VID 0x0951
#define HYPERX_ALLOY_ORIGINS_CORE_PID 0x16E6
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs (HP) |
\*---------------------------------------------------------*/
#define HP_KEYBOARD_VID 0x03F0
#define HYPERX_ALLOY_ORIGINS_CORE_HP_PID 0x098F
DetectedControllers DetectHyperXAlloyOriginsCore(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyOriginsCoreController* controller = new HyperXAlloyOriginsCoreController(dev, info, name);
RGBController_HyperXAlloyOriginsCore* rgb_controller = new RGBController_HyperXAlloyOriginsCore(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core", DetectHyperXAlloyOriginsCore, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_PID, 2);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core (HP)", DetectHyperXAlloyOriginsCore, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_HP_PID, 2);
@@ -0,0 +1,39 @@
/*---------------------------------------------------------*\
| HyperXEve1800ControllerDetect.cpp |
| |
| Driver for HyperX Eve 1800 keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HyperXEve1800Controller.h"
#include "RGBController_HyperXEve1800.h"
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs (HP) |
\*---------------------------------------------------------*/
#define HP_KEYBOARD_VID 0x03F0
#define HYPERX_EVE_1800_HP_PID 0x08C2
DetectedControllers DetectHyperXEve1800(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXEve1800Controller* controller = new HyperXEve1800Controller(dev, info->path, name);
RGBController_HyperXEve1800* rgb_controller = new RGBController_HyperXEve1800(controller);
detected_controllers.push_back(rgb_controller);
}
return detected_controllers;
}
REGISTER_HID_DETECTOR_I("HyperX Eve 1800 (HP)", DetectHyperXEve1800, HP_KEYBOARD_VID, HYPERX_EVE_1800_HP_PID, 2);
@@ -1,242 +0,0 @@
/*---------------------------------------------------------*\
| HyperXKeyboardControllerDetect.cpp |
| |
| Driver for HyperX keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HyperXAlloyEliteController.h"
#include "HyperXAlloyElite2Controller.h"
#include "HyperXAlloyFPSController.h"
#include "HyperXAlloyOriginsController.h"
#include "HyperXAlloyOriginsCoreController.h"
#include "HyperXAlloyOrigins60and65Controller.h"
#include "HyperXEve1800Controller.h"
#include "HyperXOrigins2_65Controller.h"
#include "RGBController_HyperXAlloyElite.h"
#include "RGBController_HyperXAlloyElite2.h"
#include "RGBController_HyperXAlloyFPS.h"
#include "RGBController_HyperXAlloyOrigins.h"
#include "RGBController_HyperXAlloyOriginsCore.h"
#include "RGBController_HyperXAlloyOrigins60and65.h"
#include "RGBController_HyperXEve1800.h"
#include "RGBController_HyperXOrigins2_65.h"
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs |
\*---------------------------------------------------------*/
#define HYPERX_KEYBOARD_VID 0x0951
#define HYPERX_ALLOY_ELITE_PID 0x16BE
#define HYPERX_ALLOY_ELITE_2_PID 0x1711
#define HYPERX_ALLOY_FPS_RGB_PID 0x16DC
#define HYPERX_ALLOY_ORIGINS_PID 0x16E5
#define HYPERX_ALLOY_ORIGINS_CORE_PID 0x16E6
#define HYPERX_ALLOY_ORIGINS_60_PID 0x1734
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs (HP) |
\*---------------------------------------------------------*/
#define HP_KEYBOARD_VID 0x03F0
#define HYPERX_ALLOY_ELITE_2_HP_PID 0x058F
#define HYPERX_ALLOY_ORIGINS_60_HP_PID 0x0C8E
#define HYPERX_ALLOY_ORIGINS_65_HP_PID 0x038F
#define HYPERX_ALLOY_ORIGINS_CORE_HP_PID 0x098F
#define HYPERX_ALLOY_ORIGINS_HP_PID 0x0591
#define HYPERX_EVE_1800_HP_PID 0x08C2
#define HYPERX_ORIGINS_2_65_HP_PID 0x0CC2
AlloyOrigins60and65MappingLayoutType GetAlloyOrigins60and65MappingLayoutType(int pid)
{
switch(pid)
{
case HYPERX_ALLOY_ORIGINS_60_PID:
case HYPERX_ALLOY_ORIGINS_60_HP_PID:
return ALLOY_ORIGINS_60_LAYOUT;
case HYPERX_ALLOY_ORIGINS_65_HP_PID:
return ALLOY_ORIGINS_65_LAYOUT;
default:
return ALLOY_ORIGINS_60_LAYOUT;
}
}
DetectedControllers DetectHyperXAlloyElite(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyEliteController* controller = new HyperXAlloyEliteController(dev, info->path, name);
RGBController_HyperXAlloyElite* rgb_controller = new RGBController_HyperXAlloyElite(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectHyperXAlloyElite2(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyElite2Controller* controller = new HyperXAlloyElite2Controller(dev, info->path, name);
RGBController_HyperXAlloyElite2* rgb_controller = new RGBController_HyperXAlloyElite2(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectHyperXAlloyFPS(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyFPSController* controller = new HyperXAlloyFPSController(dev, info->path, name);
RGBController_HyperXAlloyFPS* rgb_controller = new RGBController_HyperXAlloyFPS(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectHyperXAlloyOrigins(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyOriginsController* controller = new HyperXAlloyOriginsController(dev, info->path, name);
RGBController_HyperXAlloyOrigins* rgb_controller = new RGBController_HyperXAlloyOrigins(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectHyperXAlloyOriginsCore(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyOriginsCoreController* controller = new HyperXAlloyOriginsCoreController(dev, info, name);
RGBController_HyperXAlloyOriginsCore* rgb_controller = new RGBController_HyperXAlloyOriginsCore(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectHyperXAlloyOrigins60and65(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXAlloyOrigins60and65Controller* controller = new HyperXAlloyOrigins60and65Controller(dev, info->path, name);
AlloyOrigins60and65MappingLayoutType layout = GetAlloyOrigins60and65MappingLayoutType(info->product_id);
RGBController_HyperXAlloyOrigins60and65* rgb_controller = new RGBController_HyperXAlloyOrigins60and65(controller, layout);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectHyperXOrigins2_65(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXOrigins2_65Controller* controller = new HyperXOrigins2_65Controller(dev, info->path, name);
RGBController_HyperXOrigins2_65* rgb_controller = new RGBController_HyperXOrigins2_65(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectHyperXEve1800(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXEve1800Controller* controller = new HyperXEve1800Controller(dev, info->path, name);
RGBController_HyperXEve1800* rgb_controller = new RGBController_HyperXEve1800(controller);
detected_controllers.push_back(rgb_controller);
}
return detected_controllers;
}
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite RGB", DetectHyperXAlloyElite, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_PID, 2, 0xFF01);
REGISTER_HID_DETECTOR_IP("HyperX Alloy FPS RGB", DetectHyperXAlloyFPS, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_FPS_RGB_PID, 2, 0xFF01);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core", DetectHyperXAlloyOriginsCore, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_PID, 2);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core (HP)", DetectHyperXAlloyOriginsCore, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_HP_PID, 2);
REGISTER_HID_DETECTOR_I("HyperX Origins 2 65 (HP)", DetectHyperXOrigins2_65, HP_KEYBOARD_VID, HYPERX_ORIGINS_2_65_HP_PID, 3);
REGISTER_HID_DETECTOR_I("HyperX Eve 1800 (HP)", DetectHyperXEve1800, HP_KEYBOARD_VID, HYPERX_EVE_1800_HP_PID, 2);
#ifdef _WIN32
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 3);
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite 2", DetectHyperXAlloyElite2, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_PID, 3, 0xFF90);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60", DetectHyperXAlloyOrigins60and65, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_PID, 3);
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite 2 (HP)", DetectHyperXAlloyElite2, HP_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_HP_PID, 3, 0xFF90);
REGISTER_HID_DETECTOR_IP("HyperX Alloy Origins (HP)", DetectHyperXAlloyOrigins, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_HP_PID, 3, 0xFF90);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_HP_PID, 3);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 65 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_65_HP_PID, 3);
#else
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Elite 2", DetectHyperXAlloyElite2, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60", DetectHyperXAlloyOrigins60and65, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Elite 2 (HP)", DetectHyperXAlloyElite2, HP_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_HP_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins (HP)", DetectHyperXAlloyOrigins, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_HP_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_HP_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 65 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_65_HP_PID, 0);
#endif
@@ -0,0 +1,39 @@
/*---------------------------------------------------------*\
| HyperXOrigins2_65ControllerDetect.cpp |
| |
| Driver for HyperX Origins 2 65 keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "HyperXOrigins2_65Controller.h"
#include "RGBController_HyperXOrigins2_65.h"
/*---------------------------------------------------------*\
| HyperX keyboard vendor and product IDs (HP) |
\*---------------------------------------------------------*/
#define HP_KEYBOARD_VID 0x03F0
#define HYPERX_ORIGINS_2_65_HP_PID 0x0CC2
DetectedControllers DetectHyperXOrigins2_65(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
HyperXOrigins2_65Controller* controller = new HyperXOrigins2_65Controller(dev, info->path, name);
RGBController_HyperXOrigins2_65* rgb_controller = new RGBController_HyperXOrigins2_65(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_I("HyperX Origins 2 65 (HP)", DetectHyperXOrigins2_65, HP_KEYBOARD_VID, HYPERX_ORIGINS_2_65_HP_PID, 3);
@@ -768,7 +768,7 @@ void RGBController_LogitechHIDPP20::SetupZones()
perkey_zone.leds_min = klm.GetKeyCount();
perkey_zone.leds_max = klm.GetKeyCount();
perkey_zone.leds_count = klm.GetKeyCount();
perkey_zone.matrix_map = klm.GetKeyMap();
perkey_zone.matrix_map = klm.GetKeyMap(KEYBOARD_MAP_FILL_TYPE_COUNT);
zones.push_back(perkey_zone);
for(unsigned int i = 0; i < klm.GetKeyCount(); i++)
@@ -41,7 +41,7 @@ REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Gaming X Slim",
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 SUPER Gaming X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 SUPER Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 SUPER Gaming X Slim MLG", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_MLG_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 SUPER Gaming X Slim MLG", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_MLG_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti Gaming X Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, MSI_SUB_VEN, MSI_RTX4070TI_GAMING_X_TRIO_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti Gaming X Trio White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, MSI_SUB_VEN, MSI_RTX4070TI_GAMING_X_TRIO_WHITE_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, MSI_SUB_VEN, MSI_RTX4070TI_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68);
@@ -50,6 +50,7 @@ REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti SUPER Gaming X Trio White",
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti SUPER Gaming Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, MSI_SUB_VEN, MSI_RTX4070TIS_GAMING_SLIM_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti SUPER Gaming X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, MSI_SUB_VEN, MSI_RTX4070TIS_GAMING_X_SLIM_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti SUPER Gaming White X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, MSI_SUB_VEN, MSI_RTX4070TIS_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti SUPER Gaming White X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, MSI_SUB_VEN, MSI_RTX4070TIS_GAMING_X_SLIM_WHITE_SUB_DEV_2, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, MSI_SUB_VEN, MSI_RTX4080S_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 Gaming X Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, MSI_SUB_VEN, MSI_RTX4080_GAMING_X_TRIO_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 Gaming X Trio White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, MSI_SUB_VEN, MSI_RTX4080_GAMING_X_TRIO_WHITE_SUB_DEV, 0x68);
@@ -68,7 +69,7 @@ REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5070 Ti VANGUARD SOC",
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 Gaming Trio OC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_GAMING_TRIO_OC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 VANGUARD OC SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_VANGUARD_SOC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 SUPRIM SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_SUPRIM_SOC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 SUPRIM LIQUID SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_SUPRIM_LIQUID_SOC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 SUPRIM LIQUID SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_SUPRIM_LIQUID_SOC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 Gaming Trio OC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_GAMING_TRIO_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 VANGUARD SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_VANGUARD_SOC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 SUPRIM SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_SUPRIM_SOC_SUB_DEV, 0x68);
@@ -1,18 +1,40 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| MSIMonitorController.cpp |
| |
| RGBController for MSI monitor (gaming controller) |
| Driver for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 June 4 |
| Ken Sanislo 2026 July 20 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <chrono>
#include <string.h>
#include <thread>
#include "MSIMonitorController.h"
#include "StringUtils.h"
/*---------------------------------------------------------*\
| Feature reports are control transfers and this panel |
| holds one outstanding for tens of milliseconds. Back |
| to back writes pin the shared control pipe at full |
| duty and starve other non periodic traffic: a device |
| with no interrupt OUT endpoint writes by SET_REPORT |
| and stops answering, while its isochronous endpoints |
| keep running. |
| |
| Idle a fixed interval after each write to leave the |
| control pipe free. |
\*---------------------------------------------------------*/
void MSIMonitorController::SendFeatureReport(uint8_t *data, size_t length)
{
hid_send_feature_report(dev, data, length);
std::this_thread::sleep_for(std::chrono::milliseconds(MSI_MONITOR_PIPE_IDLE_MS));
}
MSIMonitorController::MSIMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name)
{
dev = dev_handle;
@@ -117,5 +139,125 @@ void MSIMonitorController::Set(uint8_t mode_value, const std::vector<RGBColor> c
| Send the data (1 packet) |
\*---------------------------------------------------------*/
hid_send_feature_report(dev, data, MSI_MONITOR_PACKET_SIZE);
SendFeatureReport(data, MSI_MONITOR_PACKET_SIZE);
}
uint8_t MSIMonitorController::GetLayoutVersion()
{
/*-----------------------------------------------------*\
| Ask the MCU which buffer layout it speaks. Request |
| is [0x01][opcode] zero padded, reply is [0x01][0x5A] |
| then data with the version at byte 3. |
\*-----------------------------------------------------*/
uint8_t request[MSI_MONITOR_QUERY_PACKET_SIZE];
uint8_t reply[MSI_MONITOR_QUERY_PACKET_SIZE];
/*-----------------------------------------------------*\
| Unimplemented opcodes never answer, so a reply left |
| queued by an earlier exchange would be read here and |
| parsed as this query's answer. Drain first. |
\*-----------------------------------------------------*/
while(hid_read_timeout(dev, reply, MSI_MONITOR_QUERY_PACKET_SIZE, 0) > 0)
{
continue;
}
memset(request, 0x00, MSI_MONITOR_QUERY_PACKET_SIZE);
request[0] = MSI_MONITOR_QUERY_REPORT_ID;
request[1] = MSI_MONITOR_OPCODE_GET_VERSION;
if(hid_write(dev, request, MSI_MONITOR_QUERY_PACKET_SIZE) < 0)
{
return(MSI_MONITOR_VERSION_UNKNOWN);
}
memset(reply, 0x00, MSI_MONITOR_QUERY_PACKET_SIZE);
if(hid_read_timeout(dev, reply, MSI_MONITOR_QUERY_PACKET_SIZE, 500) <= 0)
{
return(MSI_MONITOR_VERSION_UNKNOWN);
}
if(reply[1] != MSI_MONITOR_QUERY_ACK)
{
return(MSI_MONITOR_VERSION_UNKNOWN);
}
return(reply[3]);
}
void MSIMonitorController::SetMode72(uint8_t mode_value, uint8_t speed, uint8_t brightness, RGBColor color1, RGBColor color2, bool user_palette, bool save, const std::vector<RGBColor>& led_colors, bool fill_both_arrays)
{
uint8_t data[MSI_MONITOR_72_PACKET_SIZE];
memset(data, 0x00, MSI_MONITOR_72_PACKET_SIZE);
data[0] = 0x72;
/*-----------------------------------------------------*\
| Identical 11 byte control blocks at index 1 and 12 |
\*-----------------------------------------------------*/
for(unsigned int base = 1; base <= 12; base += 11)
{
data[base + 0] = mode_value;
data[base + 1] = RGBGetRValue(color1);
data[base + 2] = RGBGetGValue(color1);
data[base + 3] = RGBGetBValue(color1);
data[base + 4] = speed;
data[base + 5] = brightness;
data[base + 6] = RGBGetRValue(color2);
data[base + 7] = RGBGetGValue(color2);
data[base + 8] = RGBGetBValue(color2);
data[base + 9] = user_palette ? 0x80 : 0x00;
data[base + 10] = 0x00;
}
/*-----------------------------------------------------*\
| The engine paints from the second inline colour array |
| at index 95, not from Colour1 and not from the first |
| array at index 23: on a 322URX a buffer carrying only |
| Colour1 renders black, and walking one lit slot |
| through the buffer only lights LEDs for the second |
| array. That pairs with the control block at index 12 |
| being the operative one. Built in palette modes |
| leave the array clear so the firmware effect is not |
| painted over. |
| |
| fill_both_arrays also writes the first array at index |
| 23, for the dual block versions (11/13/15) whose two |
| control blocks each drive their own run. On a single |
| block layout the first array is inert, so filling it |
| is harmless. Those versions also route colour index |
| above 15 to a separate DragonShield emblem region not |
| handled here - one reason they stay disabled. |
\*-----------------------------------------------------*/
if(user_palette)
{
for(unsigned int slot = 0; slot < MSI_MONITOR_72_ARRAY_SLOTS && slot < led_colors.size(); slot++)
{
unsigned int offset_b = MSI_MONITOR_72_ARRAY_B_INDEX + (slot * 3);
data[offset_b + 0] = RGBGetRValue(led_colors[slot]);
data[offset_b + 1] = RGBGetGValue(led_colors[slot]);
data[offset_b + 2] = RGBGetBValue(led_colors[slot]);
if(fill_both_arrays)
{
unsigned int offset_a = MSI_MONITOR_72_ARRAY_A_INDEX + (slot * 3);
data[offset_a + 0] = RGBGetRValue(led_colors[slot]);
data[offset_a + 1] = RGBGetGValue(led_colors[slot]);
data[offset_a + 2] = RGBGetBValue(led_colors[slot]);
}
}
}
/*-----------------------------------------------------*\
| Final byte is store to flash: 0x01 persists across |
| power cycles, 0x00 applies without a flash write. |
| Keep 0x00 for streaming to avoid flicker and wear. |
\*-----------------------------------------------------*/
data[MSI_MONITOR_72_STORE_INDEX] = save ? 0x01 : 0x00;
SendFeatureReport(data, MSI_MONITOR_72_PACKET_SIZE);
}
@@ -1,9 +1,10 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| MSIMonitorController.h |
| |
| RGBController for MSI monitor (gaming controller) |
| Driver for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 June 1 |
| Ken Sanislo 2026 July 20 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -18,6 +19,51 @@
#define MSI_MONITOR_LEDS 9
#define MSI_MONITOR_PACKET_SIZE 78
/*---------------------------------------------------------*\
| This panel holds each feature report outstanding for |
| tens of ms while its MCU applies the frame, pinning the |
| shared control pipe. Idle briefly after each write so |
| other non periodic traffic on the bus is not starved. |
| See SendFeatureReport. |
\*---------------------------------------------------------*/
#define MSI_MONITOR_PIPE_IDLE_MS 5
/*---------------------------------------------------------*\
| 0x72 layout (dual control block, e.g. MPG 322URX QD-OLED) |
\*---------------------------------------------------------*/
#define MSI_MONITOR_72_PACKET_SIZE 168
#define MSI_MONITOR_72_STORE_INDEX 167
#define MSI_MONITOR_72_ARRAY_A_INDEX 23
#define MSI_MONITOR_72_ARRAY_B_INDEX 95
#define MSI_MONITOR_72_ARRAY_SLOTS 24
/*---------------------------------------------------------*\
| Fill order the firmware expects (vendor Direction enum). |
| Only the physical origin differs; index 0 is that end. |
| The controller mirrors RightToLeft into left origin so |
| OpenRGB LED 0 is always the left/start. Circle and Ctype |
| are non linear shapes: driven as a plain line for now, |
| they want a custom layout once someone maps the shape. |
\*---------------------------------------------------------*/
enum
{
MSI_MONITOR_72_DIR_RIGHT_TO_LEFT = 0,
MSI_MONITOR_72_DIR_LEFT_TO_RIGHT = 1,
MSI_MONITOR_72_DIR_UP_TO_DOWN = 2,
MSI_MONITOR_72_DIR_CIRCLE = 3,
MSI_MONITOR_72_DIR_CTYPE = 4,
};
/*---------------------------------------------------------*\
| Layout version query rides the short report channel: |
| write [0x01][opcode], read [0x01][0x5A][data] |
\*---------------------------------------------------------*/
#define MSI_MONITOR_QUERY_PACKET_SIZE 64
#define MSI_MONITOR_QUERY_REPORT_ID 0x01
#define MSI_MONITOR_QUERY_ACK 0x5A
#define MSI_MONITOR_OPCODE_GET_VERSION 0xB0
#define MSI_MONITOR_VERSION_UNKNOWN 0
enum
{
MSI_MONITOR_OFF_MODE_VALUE = 0x00,
@@ -31,6 +77,53 @@ enum
MSI_MONITOR_RANDOM_MODE_VALUE = 0x1F
};
/*---------------------------------------------------------*\
| 0x72 layout mode values. These match the MSI Mystic |
| Light motherboard mode enum value for value (see |
| MSIMysticLightController/MSIMysticLightCommon.h): the |
| monitor runs the same Nuvoton Mystic Light engine, so the |
| effect numbering is shared with its motherboard sibling. |
| Values 10-14 and 32 are the audio modes, not exposed |
| here (they need the music level feed). |
\*---------------------------------------------------------*/
enum
{
MSI_MONITOR_72_MODE_OFF = 0,
MSI_MONITOR_72_MODE_STATIC = 1,
MSI_MONITOR_72_MODE_BREATHING = 2,
MSI_MONITOR_72_MODE_FLASHING = 3,
MSI_MONITOR_72_MODE_DOUBLE_FLASHING = 4,
MSI_MONITOR_72_MODE_LIGHTNING = 5,
MSI_MONITOR_72_MODE_SCANNER = 6,
MSI_MONITOR_72_MODE_METEOR = 7,
MSI_MONITOR_72_MODE_WATER_DROP = 8,
MSI_MONITOR_72_MODE_RAINBOW_MSI = 9,
MSI_MONITOR_72_MODE_COLOR_RING = 15,
MSI_MONITOR_72_MODE_PLANETARY = 16,
MSI_MONITOR_72_MODE_DOUBLE_METEOR = 17,
MSI_MONITOR_72_MODE_ENERGY = 18,
MSI_MONITOR_72_MODE_BLINK = 19,
MSI_MONITOR_72_MODE_CLOCK = 20,
MSI_MONITOR_72_MODE_COLOR_PULSE = 21,
MSI_MONITOR_72_MODE_COLOR_SHIFT = 22,
MSI_MONITOR_72_MODE_COLOR_WAVE = 23,
MSI_MONITOR_72_MODE_MARQUEE = 24,
MSI_MONITOR_72_MODE_RAINBOW = 25,
MSI_MONITOR_72_MODE_RAINBOW_WAVE = 26,
MSI_MONITOR_72_MODE_VISOR = 27,
MSI_MONITOR_72_MODE_JRAINBOW = 28,
MSI_MONITOR_72_MODE_RAINBOW_FLASH = 29,
MSI_MONITOR_72_MODE_RAINBOW_DBLFLASH = 30,
MSI_MONITOR_72_MODE_RAINBOW_LINE = 33,
};
enum
{
MSI_MONITOR_72_SPEED_LOW = 0,
MSI_MONITOR_72_SPEED_MEDIUM = 1,
MSI_MONITOR_72_SPEED_HIGH = 2,
};
class MSIMonitorController
{
public:
@@ -44,7 +137,12 @@ public:
void Set(uint8_t mode_value, const std::vector<RGBColor> colors, uint8_t last_bit);
uint8_t GetLayoutVersion();
void SetMode72(uint8_t mode_value, uint8_t speed, uint8_t brightness, RGBColor color1, RGBColor color2, bool user_palette, bool save, const std::vector<RGBColor>& led_colors, bool fill_both_arrays);
private:
void SendFeatureReport(uint8_t *data, size_t length);
hid_device *dev;
std::string description;
std::string location;
@@ -1,5 +1,5 @@
/*---------------------------------------------------------*\
| MSIKeyboardControllerDetect.cpp |
| MSIMonitorControllerDetect.cpp |
| |
| Detector for MSI monitor (MSI Gaming Controller) |
| |
@@ -10,10 +10,107 @@
#include "DetectionManager.h"
#include "MSIMonitorController.h"
#include "RGBController_MSIMonitor.h"
#include "RGBController_MSIMonitor72.h"
#define MSI_USB_VID 0x1462
#define MSI_USB_PID 0x3FA4
/*---------------------------------------------------------*\
| Vendor layout table (from the version -> layout map). |
| The firmware reports its version to the 0xB0 query but |
| not a LED count, so count/direction/block-count come |
| from here. Fields: version, LED count, fill direction |
| (MSI_MONITOR_72_DIR_*), dual_block. |
| |
| Every entry here is a single linear run driven through |
| the 168 byte 0x72 buffer. Direction mirrors the fill so |
| OpenRGB LED 0 is the left/start on every model. Circle |
| and Ctype have no simple line mapping - they are driven |
| as a plain line here and should get a custom per model |
| layout once an owner maps the physical shape. |
| |
| Only version 34 is hardware verified (MPG 322URX). The |
| rest are derived from the vendor table: same buffer, LED |
| count and direction are the only differences, so effect |
| and single colour modes are correct by construction. |
| |
| Left out on purpose: |
| - dual_block versions 11/13/15 are stubbed but disabled |
| (see the commented block); their two control blocks |
| drive two runs and the per LED split is unconfirmed. |
| - Advanced/0x82 versions 31/47 need the 0x30 bulk report |
| this controller does not implement. |
| A version not matched here falls through to the legacy |
| 0x71 controller, home of the MAG272 class. |
\*---------------------------------------------------------*/
struct MSIMonitor72Layout
{
uint8_t version;
unsigned int leds;
unsigned int direction;
bool dual_block;
};
static const MSIMonitor72Layout msi_monitor_72_layouts[] =
{
{ 34, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG271QRXQD_OLED (MPG 322URX reports this) - verified */
{ 5, 12, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MAG272R */
{ 8, 24, MSI_MONITOR_72_DIR_CIRCLE, false }, /* MAG301CR - Circle, wants custom layout */
{ 10, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG273R */
{ 12, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG271VCR */
{ 14, 14, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* NXG253R */
{ 17, 8, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MAG2x5R */
{ 20, 12, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG301RF */
{ 21, 18, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG341QR */
{ 22, 24, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG321QRF */
{ 24, 12, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG321QR */
{ 25, 24, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG321UR */
{ 28, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG273CQR */
{ 29, 24, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MAG401QR */
{ 30, 21, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MAG325CQRF */
{ 32, 7, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG274QRF */
{ 33, 7, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG274URFQD */
{ 36, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG275CQRXF */
{ 37, 22, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG345CQRD */
{ 38, 20, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG325CQRFQDE2 */
{ 39, 8, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MPG321CURX_QD_OLED */
{ 40, 12, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MAG322URDFE16 */
{ 41, 14, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, false }, /* MPG242RX60N */
{ 42, 22, MSI_MONITOR_72_DIR_CTYPE, false }, /* MPG274URDFWE16 - Ctype, wants custom layout */
{ 43, 21, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG346CQRFX24 */
{ 44, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG271QRQD_OLEDX50 */
{ 46, 9, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, false }, /* MPG341CQRQD_OLEDX36 */
/*-----------------------------------------------------------------*\
| Dual block - stubbed but DISABLED. The dual_block path fills |
| both colour arrays so uniform colour modes are correct, but each |
| block drives its own run and the per LED split is unconfirmed, so |
| Direct is a guess. These versions also map colour index above 15 |
| to a separate DragonShield emblem region (unhandled). An owner |
| should enable, test, and ideally give it a custom layout: |
| { 11, 16, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, true }, MPG273CQR |
| { 13, 21, MSI_MONITOR_72_DIR_LEFT_TO_RIGHT, true }, MPG343CQR |
| { 15, 18, MSI_MONITOR_72_DIR_RIGHT_TO_LEFT, true }, MPG323CQR |
| |
| Advanced/0x82 - not addable here, need the 0x30 bulk report: |
| version 31, 40 LEDs, MEG342CQD_OLED |
| version 47, 83 LEDs, 4 areas, MEG_X |
\*-----------------------------------------------------------------*/
};
static const MSIMonitor72Layout* MSIMonitorLayout(uint8_t version)
{
for(unsigned int i = 0; i < (sizeof(msi_monitor_72_layouts) / sizeof(msi_monitor_72_layouts[0])); i++)
{
if(msi_monitor_72_layouts[i].version == version)
{
return(&msi_monitor_72_layouts[i]);
}
}
return(nullptr);
}
DetectedControllers DetectMSIMonitorController(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
@@ -23,10 +120,41 @@ DetectedControllers DetectMSIMonitorController(hid_device_info* info, const std:
if(dev)
{
MSIMonitorController* controller = new MSIMonitorController(dev, *info, name);
RGBController_MSIMonitor* rgb_controller = new RGBController_MSIMonitor(controller);
/*-------------------------------------------------*\
| One VID/PID covers several buffer layouts, so ask |
| the MCU which one it speaks. A version in the |
| 0x72 table selects the 168 byte layout and its |
| LED count; anything else keeps the legacy 0x71 |
| path, which is what the older monitors answer to. |
\*-------------------------------------------------*/
MSIMonitorController* controller = new MSIMonitorController(dev, *info, name);
detected_controllers.push_back(rgb_controller);
uint8_t version = controller->GetLayoutVersion();
const MSIMonitor72Layout* layout = MSIMonitorLayout(version);
if(layout != nullptr)
{
detected_controllers.push_back(new RGBController_MSIMonitor72(controller, layout->leds, layout->direction, layout->dual_block));
}
else
{
/*-------------------------------------------------*\
| 0x71 family (78 byte buffer), handled by the |
| legacy controller as a plain line. For an owner |
| who wants to bring one onto this controller: same |
| operative block 12, but the second colour array |
| is at index 50 (not 95) and store-flash at 77. |
| RightToLeft mirroring applies. version, LEDs, |
| direction: |
| 1, 9, RightToLeft (early/generic) |
| 2, 7, LeftToRight (malformed stub, skip) |
| 3, 9, RightToLeft (early/generic) |
| 4, 9, LeftToRight (early/generic) |
| 6, 9, UpToDown MAG270CR |
| 7, 7, LeftToRight MAG251RX |
\*-------------------------------------------------*/
detected_controllers.push_back(new RGBController_MSIMonitor(controller));
}
}
return(detected_controllers);
@@ -9,12 +9,8 @@
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <chrono>
#include <thread>
#include "RGBController_MSIMonitor.h"
using namespace std::chrono_literals;
/**------------------------------------------------------------------*\
@name MSIMonitor
@category Accessory
@@ -112,10 +108,6 @@ RGBController_MSIMonitor::RGBController_MSIMonitor(MSIMonitorController* control
RGBController_MSIMonitor::~RGBController_MSIMonitor()
{
keepalive_thread_run = 0;
keepalive_thread->join();
delete keepalive_thread;
Shutdown();
delete controller;
@@ -145,7 +137,6 @@ void RGBController_MSIMonitor::SetupZones()
void RGBController_MSIMonitor::DeviceUpdateLEDs()
{
last_update_time = std::chrono::steady_clock::now();
controller->Set(modes[active_mode].value, colors, active_mode == 0 ? 0x00 : 0x01);
}
@@ -1,5 +1,5 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| RGBController_MSIMonitor.h |
| |
| RGBController for MSI monitor (gaming controller) |
| |
@@ -11,7 +11,6 @@
#pragma once
#include <chrono>
#include "RGBController.h"
#include "MSIMonitorController.h"
@@ -31,9 +30,4 @@ public:
private:
MSIMonitorController* controller;
std::thread* keepalive_thread;
std::atomic<bool> keepalive_thread_run;
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
void KeepaliveThread();
};
@@ -0,0 +1,570 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor72.cpp |
| |
| RGBController for MSI monitors using the 0x72 dual |
| control block layout (MPG 322URX QD-OLED) |
| |
| Ken Sanislo 19 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <algorithm>
#include "RGBController_MSIMonitor72.h"
/**------------------------------------------------------------------*\
@name MSIMonitor72
@category Accessory
@type USB
@save :robot:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectMSIMonitorController
@comment Developed with MSI MPG 322URX QD-OLED
\*-------------------------------------------------------------------*/
RGBController_MSIMonitor72::RGBController_MSIMonitor72(MSIMonitorController* controller_ptr, unsigned int count, unsigned int direction, bool dual)
{
controller = controller_ptr;
led_count = count;
reverse_order = (direction == MSI_MONITOR_72_DIR_RIGHT_TO_LEFT);
dual_block = dual;
name = controller->GetNameString();
vendor = "MSI";
type = DEVICE_TYPE_MONITOR;
description = "MSI Monitor (Gaming Controller)";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
/*-----------------------------------------------------*\
| This layout has no Advanced mode, so Direct is Static |
| driven from the inline per LED colour arrays. |
\*-----------------------------------------------------*/
mode Direct;
Direct.name = "Direct";
Direct.value = MSI_MONITOR_72_MODE_STATIC;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Direct.brightness_min = 0;
Direct.brightness_max = 100;
Direct.brightness = 100;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = MSI_MONITOR_72_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Static.colors_min = 1;
Static.colors_max = 1;
Static.colors.resize(1);
Static.brightness_min = 0;
Static.brightness_max = 100;
Static.brightness = 100;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Static);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = MSI_MONITOR_72_MODE_BREATHING;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.colors.resize(1);
Breathing.speed_min = MSI_MONITOR_72_SPEED_LOW;
Breathing.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Breathing.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Breathing.brightness_min = 0;
Breathing.brightness_max = 100;
Breathing.brightness = 100;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Breathing);
mode Flashing;
Flashing.name = "Flashing";
Flashing.value = MSI_MONITOR_72_MODE_FLASHING;
Flashing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Flashing.colors_min = 2;
Flashing.colors_max = 2;
Flashing.colors.resize(2);
Flashing.speed_min = MSI_MONITOR_72_SPEED_LOW;
Flashing.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Flashing.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Flashing.brightness_min = 0;
Flashing.brightness_max = 100;
Flashing.brightness = 100;
Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Flashing);
mode DoubleFlashing;
DoubleFlashing.name = "Double Flashing";
DoubleFlashing.value = MSI_MONITOR_72_MODE_DOUBLE_FLASHING;
DoubleFlashing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
DoubleFlashing.colors_min = 2;
DoubleFlashing.colors_max = 2;
DoubleFlashing.colors.resize(2);
DoubleFlashing.speed_min = MSI_MONITOR_72_SPEED_LOW;
DoubleFlashing.speed_max = MSI_MONITOR_72_SPEED_HIGH;
DoubleFlashing.speed = MSI_MONITOR_72_SPEED_MEDIUM;
DoubleFlashing.brightness_min = 0;
DoubleFlashing.brightness_max = 100;
DoubleFlashing.brightness = 100;
DoubleFlashing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(DoubleFlashing);
mode Lightning;
Lightning.name = "Lightning";
Lightning.value = MSI_MONITOR_72_MODE_LIGHTNING;
Lightning.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Lightning.colors_min = 1;
Lightning.colors_max = 1;
Lightning.colors.resize(1);
Lightning.speed_min = MSI_MONITOR_72_SPEED_LOW;
Lightning.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Lightning.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Lightning.brightness_min = 0;
Lightning.brightness_max = 100;
Lightning.brightness = 100;
Lightning.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Lightning);
mode Scanner;
Scanner.name = "Scanner";
Scanner.value = MSI_MONITOR_72_MODE_SCANNER;
Scanner.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Scanner.colors_min = 1;
Scanner.colors_max = 1;
Scanner.colors.resize(1);
Scanner.speed_min = MSI_MONITOR_72_SPEED_LOW;
Scanner.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Scanner.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Scanner.brightness_min = 0;
Scanner.brightness_max = 100;
Scanner.brightness = 100;
Scanner.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Scanner);
mode Meteor;
Meteor.name = "Meteor";
Meteor.value = MSI_MONITOR_72_MODE_METEOR;
Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Meteor.colors_min = 1;
Meteor.colors_max = 1;
Meteor.colors.resize(1);
Meteor.speed_min = MSI_MONITOR_72_SPEED_LOW;
Meteor.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Meteor.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Meteor.brightness_min = 0;
Meteor.brightness_max = 100;
Meteor.brightness = 100;
Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Meteor);
mode WaterDrop;
WaterDrop.name = "Water Drop";
WaterDrop.value = MSI_MONITOR_72_MODE_WATER_DROP;
WaterDrop.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
WaterDrop.colors_min = 1;
WaterDrop.colors_max = 1;
WaterDrop.colors.resize(1);
WaterDrop.speed_min = MSI_MONITOR_72_SPEED_LOW;
WaterDrop.speed_max = MSI_MONITOR_72_SPEED_HIGH;
WaterDrop.speed = MSI_MONITOR_72_SPEED_MEDIUM;
WaterDrop.brightness_min = 0;
WaterDrop.brightness_max = 100;
WaterDrop.brightness = 100;
WaterDrop.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(WaterDrop);
mode RainbowMSI;
RainbowMSI.name = "Rainbow";
RainbowMSI.value = MSI_MONITOR_72_MODE_RAINBOW_MSI;
RainbowMSI.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
RainbowMSI.speed_min = MSI_MONITOR_72_SPEED_LOW;
RainbowMSI.speed_max = MSI_MONITOR_72_SPEED_HIGH;
RainbowMSI.speed = MSI_MONITOR_72_SPEED_MEDIUM;
RainbowMSI.brightness_min = 0;
RainbowMSI.brightness_max = 100;
RainbowMSI.brightness = 100;
RainbowMSI.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowMSI);
mode ColorRing;
ColorRing.name = "Color Ring";
ColorRing.value = MSI_MONITOR_72_MODE_COLOR_RING;
ColorRing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
ColorRing.colors_min = 1;
ColorRing.colors_max = 1;
ColorRing.colors.resize(1);
ColorRing.speed_min = MSI_MONITOR_72_SPEED_LOW;
ColorRing.speed_max = MSI_MONITOR_72_SPEED_HIGH;
ColorRing.speed = MSI_MONITOR_72_SPEED_MEDIUM;
ColorRing.brightness_min = 0;
ColorRing.brightness_max = 100;
ColorRing.brightness = 100;
ColorRing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(ColorRing);
mode Planetary;
Planetary.name = "Planetary";
Planetary.value = MSI_MONITOR_72_MODE_PLANETARY;
Planetary.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Planetary.colors_min = 1;
Planetary.colors_max = 1;
Planetary.colors.resize(1);
Planetary.speed_min = MSI_MONITOR_72_SPEED_LOW;
Planetary.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Planetary.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Planetary.brightness_min = 0;
Planetary.brightness_max = 100;
Planetary.brightness = 100;
Planetary.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Planetary);
mode DoubleMeteor;
DoubleMeteor.name = "Double Meteor";
DoubleMeteor.value = MSI_MONITOR_72_MODE_DOUBLE_METEOR;
DoubleMeteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
DoubleMeteor.colors_min = 1;
DoubleMeteor.colors_max = 1;
DoubleMeteor.colors.resize(1);
DoubleMeteor.speed_min = MSI_MONITOR_72_SPEED_LOW;
DoubleMeteor.speed_max = MSI_MONITOR_72_SPEED_HIGH;
DoubleMeteor.speed = MSI_MONITOR_72_SPEED_MEDIUM;
DoubleMeteor.brightness_min = 0;
DoubleMeteor.brightness_max = 100;
DoubleMeteor.brightness = 100;
DoubleMeteor.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(DoubleMeteor);
mode Energy;
Energy.name = "Energy";
Energy.value = MSI_MONITOR_72_MODE_ENERGY;
Energy.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Energy.colors_min = 1;
Energy.colors_max = 1;
Energy.colors.resize(1);
Energy.speed_min = MSI_MONITOR_72_SPEED_LOW;
Energy.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Energy.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Energy.brightness_min = 0;
Energy.brightness_max = 100;
Energy.brightness = 100;
Energy.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Energy);
mode Blink;
Blink.name = "Blink";
Blink.value = MSI_MONITOR_72_MODE_BLINK;
Blink.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Blink.colors_min = 1;
Blink.colors_max = 1;
Blink.colors.resize(1);
Blink.speed_min = MSI_MONITOR_72_SPEED_LOW;
Blink.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Blink.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Blink.brightness_min = 0;
Blink.brightness_max = 100;
Blink.brightness = 100;
Blink.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Blink);
mode Clock;
Clock.name = "Clock";
Clock.value = MSI_MONITOR_72_MODE_CLOCK;
Clock.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Clock.colors_min = 1;
Clock.colors_max = 1;
Clock.colors.resize(1);
Clock.speed_min = MSI_MONITOR_72_SPEED_LOW;
Clock.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Clock.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Clock.brightness_min = 0;
Clock.brightness_max = 100;
Clock.brightness = 100;
Clock.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Clock);
mode ColorPulse;
ColorPulse.name = "Color Pulse";
ColorPulse.value = MSI_MONITOR_72_MODE_COLOR_PULSE;
ColorPulse.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
ColorPulse.colors_min = 1;
ColorPulse.colors_max = 1;
ColorPulse.colors.resize(1);
ColorPulse.speed_min = MSI_MONITOR_72_SPEED_LOW;
ColorPulse.speed_max = MSI_MONITOR_72_SPEED_HIGH;
ColorPulse.speed = MSI_MONITOR_72_SPEED_MEDIUM;
ColorPulse.brightness_min = 0;
ColorPulse.brightness_max = 100;
ColorPulse.brightness = 100;
ColorPulse.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(ColorPulse);
mode ColorShift;
ColorShift.name = "Color Shift";
ColorShift.value = MSI_MONITOR_72_MODE_COLOR_SHIFT;
ColorShift.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
ColorShift.colors_min = 2;
ColorShift.colors_max = 2;
ColorShift.colors.resize(2);
ColorShift.speed_min = MSI_MONITOR_72_SPEED_LOW;
ColorShift.speed_max = MSI_MONITOR_72_SPEED_HIGH;
ColorShift.speed = MSI_MONITOR_72_SPEED_MEDIUM;
ColorShift.brightness_min = 0;
ColorShift.brightness_max = 100;
ColorShift.brightness = 100;
ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(ColorShift);
mode ColorWave;
ColorWave.name = "Color Wave";
ColorWave.value = MSI_MONITOR_72_MODE_COLOR_WAVE;
ColorWave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
ColorWave.colors_min = 2;
ColorWave.colors_max = 2;
ColorWave.colors.resize(2);
ColorWave.speed_min = MSI_MONITOR_72_SPEED_LOW;
ColorWave.speed_max = MSI_MONITOR_72_SPEED_HIGH;
ColorWave.speed = MSI_MONITOR_72_SPEED_MEDIUM;
ColorWave.brightness_min = 0;
ColorWave.brightness_max = 100;
ColorWave.brightness = 100;
ColorWave.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(ColorWave);
/*-----------------------------------------------------*\
| Mode 24 marquee behaviour is model dependent. On the |
| dual block / richer boards it wants the marker bytes |
| plus a per LED array of separate colours (a user |
| defined marquee). The vendor app does not even offer |
| it for version 34, but the firmware still runs it as |
| a one colour rolling band, so we expose that. |
\*-----------------------------------------------------*/
mode Marquee;
Marquee.name = "Marquee";
Marquee.value = MSI_MONITOR_72_MODE_MARQUEE;
Marquee.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Marquee.colors_min = 1;
Marquee.colors_max = 1;
Marquee.colors.resize(1);
Marquee.speed_min = MSI_MONITOR_72_SPEED_LOW;
Marquee.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Marquee.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Marquee.brightness_min = 0;
Marquee.brightness_max = 100;
Marquee.brightness = 100;
Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Marquee);
mode SpectrumCycle;
SpectrumCycle.name = "Spectrum Cycle";
SpectrumCycle.value = MSI_MONITOR_72_MODE_RAINBOW;
SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
SpectrumCycle.speed_min = MSI_MONITOR_72_SPEED_LOW;
SpectrumCycle.speed_max = MSI_MONITOR_72_SPEED_HIGH;
SpectrumCycle.speed = MSI_MONITOR_72_SPEED_MEDIUM;
SpectrumCycle.brightness_min = 0;
SpectrumCycle.brightness_max = 100;
SpectrumCycle.brightness = 100;
SpectrumCycle.color_mode = MODE_COLORS_NONE;
modes.push_back(SpectrumCycle);
mode RainbowWave;
RainbowWave.name = "Rainbow Wave";
RainbowWave.value = MSI_MONITOR_72_MODE_RAINBOW_WAVE;
RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
RainbowWave.speed_min = MSI_MONITOR_72_SPEED_LOW;
RainbowWave.speed_max = MSI_MONITOR_72_SPEED_HIGH;
RainbowWave.speed = MSI_MONITOR_72_SPEED_MEDIUM;
RainbowWave.brightness_min = 0;
RainbowWave.brightness_max = 100;
RainbowWave.brightness = 100;
RainbowWave.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowWave);
mode Visor;
Visor.name = "Visor";
Visor.value = MSI_MONITOR_72_MODE_VISOR;
Visor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Visor.colors_min = 1;
Visor.colors_max = 1;
Visor.colors.resize(1);
Visor.speed_min = MSI_MONITOR_72_SPEED_LOW;
Visor.speed_max = MSI_MONITOR_72_SPEED_HIGH;
Visor.speed = MSI_MONITOR_72_SPEED_MEDIUM;
Visor.brightness_min = 0;
Visor.brightness_max = 100;
Visor.brightness = 100;
Visor.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Visor);
mode JRainbow;
JRainbow.name = "JRainbow";
JRainbow.value = MSI_MONITOR_72_MODE_JRAINBOW;
JRainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
JRainbow.speed_min = MSI_MONITOR_72_SPEED_LOW;
JRainbow.speed_max = MSI_MONITOR_72_SPEED_HIGH;
JRainbow.speed = MSI_MONITOR_72_SPEED_MEDIUM;
JRainbow.brightness_min = 0;
JRainbow.brightness_max = 100;
JRainbow.brightness = 100;
JRainbow.color_mode = MODE_COLORS_NONE;
modes.push_back(JRainbow);
mode RainbowFlash;
RainbowFlash.name = "Rainbow Flashing";
RainbowFlash.value = MSI_MONITOR_72_MODE_RAINBOW_FLASH;
RainbowFlash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
RainbowFlash.speed_min = MSI_MONITOR_72_SPEED_LOW;
RainbowFlash.speed_max = MSI_MONITOR_72_SPEED_HIGH;
RainbowFlash.speed = MSI_MONITOR_72_SPEED_MEDIUM;
RainbowFlash.brightness_min = 0;
RainbowFlash.brightness_max = 100;
RainbowFlash.brightness = 100;
RainbowFlash.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowFlash);
mode RainbowDblFlash;
RainbowDblFlash.name = "Rainbow Double Flashing";
RainbowDblFlash.value = MSI_MONITOR_72_MODE_RAINBOW_DBLFLASH;
RainbowDblFlash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
RainbowDblFlash.speed_min = MSI_MONITOR_72_SPEED_LOW;
RainbowDblFlash.speed_max = MSI_MONITOR_72_SPEED_HIGH;
RainbowDblFlash.speed = MSI_MONITOR_72_SPEED_MEDIUM;
RainbowDblFlash.brightness_min = 0;
RainbowDblFlash.brightness_max = 100;
RainbowDblFlash.brightness = 100;
RainbowDblFlash.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowDblFlash);
mode RainbowLine;
RainbowLine.name = "Rainbow Line";
RainbowLine.value = MSI_MONITOR_72_MODE_RAINBOW_LINE;
RainbowLine.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
RainbowLine.speed_min = MSI_MONITOR_72_SPEED_LOW;
RainbowLine.speed_max = MSI_MONITOR_72_SPEED_HIGH;
RainbowLine.speed = MSI_MONITOR_72_SPEED_MEDIUM;
RainbowLine.brightness_min = 0;
RainbowLine.brightness_max = 100;
RainbowLine.brightness = 100;
RainbowLine.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowLine);
mode Off;
Off.name = "Off";
Off.value = MSI_MONITOR_72_MODE_OFF;
Off.flags = MODE_FLAG_MANUAL_SAVE;
Off.color_mode = MODE_COLORS_NONE;
modes.push_back(Off);
SetupZones();
}
RGBController_MSIMonitor72::~RGBController_MSIMonitor72()
{
Shutdown();
delete controller;
}
void RGBController_MSIMonitor72::SetupZones()
{
zone new_zone;
new_zone.name = "Rear";
new_zone.type = ZONE_TYPE_LINEAR;
new_zone.leds_min = led_count;
new_zone.leds_max = led_count;
new_zone.leds_count = led_count;
zones.emplace_back(new_zone);
for(unsigned int led_idx = 0; led_idx < led_count; led_idx++)
{
led new_led;
new_led.name = "LED " + std::to_string(led_idx + 1);
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_MSIMonitor72::DeviceUpdateLEDs()
{
DeviceUpdateMode();
}
void RGBController_MSIMonitor72::DeviceUpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSIMonitor72::DeviceUpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSIMonitor72::ApplyMode(bool save)
{
const mode& active = modes[active_mode];
RGBColor color1 = 0;
RGBColor color2 = 0;
bool user_palette = false;
std::vector<RGBColor> led_colors;
if(active.color_mode == MODE_COLORS_PER_LED)
{
user_palette = true;
led_colors = colors;
if(led_colors.size() > 0)
{
color1 = led_colors[0];
}
}
else if(active.color_mode == MODE_COLORS_MODE_SPECIFIC)
{
user_palette = true;
if(active.colors.size() > 0)
{
color1 = active.colors[0];
if(active.colors.size() > 1)
{
color2 = active.colors[1];
}
}
led_colors.assign(led_count, color1);
}
/*-----------------------------------------------------*\
| RightToLeft layouts start at the right end, so mirror |
| the per LED order to keep OpenRGB LED 0 on the left. |
| Uniform colour modes are unaffected by the order. |
\*-----------------------------------------------------*/
if(reverse_order)
{
std::reverse(led_colors.begin(), led_colors.end());
}
controller->SetMode72((uint8_t)active.value, (uint8_t)active.speed, (uint8_t)active.brightness, color1, color2, user_palette, save, led_colors, dual_block);
}
void RGBController_MSIMonitor72::DeviceUpdateMode()
{
/*-----------------------------------------------------*\
| Updates are volatile. Persisting to flash is the |
| Save To Device button (DeviceSaveMode), so live edits |
| do not burn flash write cycles. |
\*-----------------------------------------------------*/
ApplyMode(false);
}
void RGBController_MSIMonitor72::DeviceSaveMode()
{
ApplyMode(true);
}
@@ -0,0 +1,40 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor72.h |
| |
| RGBController for MSI monitors using the 0x72 dual |
| control block layout (MPG 322URX QD-OLED) |
| |
| Ken Sanislo 19 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "MSIMonitorController.h"
class RGBController_MSIMonitor72 : public RGBController
{
public:
RGBController_MSIMonitor72(MSIMonitorController* controller_ptr, unsigned int count, unsigned int direction, bool dual);
~RGBController_MSIMonitor72();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
void ApplyMode(bool save);
MSIMonitorController* controller;
unsigned int led_count;
bool reverse_order;
bool dual_block;
};
@@ -73,6 +73,8 @@ static const std::string board_names[] =
"MSI MEG Z890 UNIFY-X (MS-7E20)",
"MSI PRO X870E-S EVO WIFI (MS-7E86)",
"MSI PRO Z890-P WIFI (MS-7E34)",
"MSI B850 GAMING PLUS WIFI PZ (MS-7E75)",
"MSI MAG X870E TOMAHAWK MAX WIFI PZ (MS-7E84)",
};
static const mystic_light_761_config board_configs[] =
@@ -103,6 +105,8 @@ static const mystic_light_761_config board_configs[] =
{ &(board_names[23]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MEG Z890 UNIFY-X (MS-7E20)
{ &(board_names[24]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO X870E-S EVO WIFI (MS-7E86)
{ &(board_names[25]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO Z890-P WIFI (MS-7E34)
{ &(board_names[26]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850 GAMING PLUS WIFI PZ (MS-7E75)
{ &(board_names[27]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG X870E TOMAHAWK MAX WIFI PZ (MS-7E84)
};
enum MSI_ZONE setup_map [] =
@@ -132,10 +132,10 @@ void RGBController_MSIMysticLight761::SetupZones()
/*-------------------------------------------------*\
| This is a fixed size zone |
\*-------------------------------------------------*/
if(((zd->zone_type != MSI_ZONE_J_RAINBOW_1)
&& (zd->zone_type != MSI_ZONE_J_RAINBOW_2)
&& (zd->zone_type != MSI_ZONE_J_RAINBOW_3)
&& (zd->zone_type != MSI_ZONE_J_CORSAIR)))
if(((zd->zone_type != MSI_ZONE_JAF)
&& (zd->zone_type != MSI_ZONE_JARGB_1)
&& (zd->zone_type != MSI_ZONE_JARGB_2)
&& (zd->zone_type != MSI_ZONE_JARGB_3)))
{
zones[zone_idx].name = zd->name;
zones[zone_idx].type = ZONE_TYPE_SINGLE;
@@ -50,6 +50,7 @@ static const nv_gpu_pci_device device_list[] =
{NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2080_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2080 FE" },
{NVIDIA_VEN, NVIDIA_RTX2080S_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2080S_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2080 SUPER FE" },
{NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2080TI_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2080 Ti FE" },
{NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2080TI_FE_CP2077_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2080 Ti Cyberpunk 2077" },
{NVIDIA_VEN, NVIDIA_TITANRTX_DEV, NVIDIA_SUB_VEN, NVIDIA_TITANRTX_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA TITAN RTX" },
{NVIDIA_VEN, NVIDIA_RTX3050_DEV, NVIDIA_SUB_VEN, GAINWARD_RTX3050_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "Gainward GeForce RTX 3050 LHR" },
{NVIDIA_VEN, NVIDIA_RTX3060_8G_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3060_8G_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "Gainward GeForce RTX 3060 Pegasus" },
@@ -76,8 +77,8 @@ static const nv_gpu_pci_device device_list[] =
{NVIDIA_VEN, NVIDIA_RTX4080S_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX4080S_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 4080 SUPER FE" },
{NVIDIA_VEN, NVIDIA_RTX4090_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX4090_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 4090 FE" },
{NVIDIA_VEN, NVIDIA_RTX4090_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX4090_FE_SUB_DEV2, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 4090 FE" },
{NVIDIA_VEN, NVIDIA_RTX5080_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX5080_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 5080 FE" },
{NVIDIA_VEN, NVIDIA_RTX5090_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX5090_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 5090 FE" },
{NVIDIA_VEN, NVIDIA_RTX5080_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX5080_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 5080 FE" },
{NVIDIA_VEN, NVIDIA_RTX5090_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX5090_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 5090 FE" },
};
DetectedControllers DetectNVIDIAIllumGPUs()
@@ -77,3 +77,4 @@ REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX6900XT", DetectPowerC
REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX6900XT Ultimate", DetectPowerColorRedDevilGPUControllersV1, AMD_GPU_VEN, AMD_NAVI21_DEV2, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX6900XT_ULTIMATE_SUB_DEV, 0x22);
REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX9070XT", DetectPowerColorRedDevilGPUControllersV2, AMD_GPU_VEN, AMD_NAVI48_DEV, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX9070XT_SUB_DEV, 0x22);
REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX9070", DetectPowerColorRedDevilGPUControllersV2, AMD_GPU_VEN, AMD_NAVI48_DEV, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX9070_SUB_DEV, 0x22);
@@ -19,7 +19,115 @@
using namespace std::chrono_literals;
QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char *path)
/*---------------------------------------------------------*\
| Keychron Patch Types |
| The Keychron protocol does not provide RGB matrix X/Y |
| coordinates, only the position in the wiring matrix. |
| For most Keychron keyboards, the wiring matrix mostly |
| matches the physical position, but there are often a |
| few misplaced LEDs. Implement a matrix patching system |
| to apply keyboard-specific patches for these misplaced |
| LEDs. |
\*---------------------------------------------------------*/
typedef struct
{
unsigned int led_idx;
unsigned int row;
unsigned int col;
} keychron_patch_entry;
typedef struct
{
unsigned short pid;
unsigned int num_entries;
const keychron_patch_entry* patch;
} keychron_patch;
/*---------------------------------------------------------*\
| Keychron K5 V2 ANSI Encoder |
\*---------------------------------------------------------*/
#define KEYCHRON_K5_V2_ANSI_RGB_PATCH_ENTRIES_COUNT \
(sizeof(keychron_k5_v2_ansi_rgb_patch_entries) / sizeof(keychron_patch_entry))
static const keychron_patch_entry keychron_k5_v2_ansi_rgb_patch_entries[] =
{
{ 18, 0, 19 },
{ 19, 0, 20 },
{ 39, 1, 19 },
{ 40, 1, 20 },
{ 60, 2, 19 },
{ 61, 2, 20 },
{ 74, 3, 12 },
{ 77, 3, 19 },
{ 89, 4, 12 },
{ 93, 4, 19 },
{ 107, 5, 19 },
{ 94, 5, 20 },
};
static const keychron_patch keychron_k5_v2_ansi_rgb_patch =
{
KEYCHRON_K5_V2_ANSI_RGB_PID,
KEYCHRON_K5_V2_ANSI_RGB_PATCH_ENTRIES_COUNT,
keychron_k5_v2_ansi_rgb_patch_entries
};
/*---------------------------------------------------------*\
| Keychron Q2 ANSI Encoder |
\*---------------------------------------------------------*/
#define KEYCHRON_Q2_ANSI_ENCODER_PATCH_ENTRIES_COUNT (sizeof(keychron_q2_ansi_encoder_patch_entries) / sizeof(keychron_patch_entry))
static const keychron_patch_entry keychron_q2_ansi_encoder_patch_entries[] =
{
{ 60, 4, 9 },
{ 61, 4, 10 },
{ 62, 4, 11 },
{ 63, 4, 12 },
{ 54, 3, 12 },
{ 55, 3, 13 },
{ 64, 4, 13 },
};
static const keychron_patch keychron_q2_ansi_encoder_patch =
{
KEYCHRON_Q2_ANSI_ENCODER_PID,
KEYCHRON_Q2_ANSI_ENCODER_PATCH_ENTRIES_COUNT,
keychron_q2_ansi_encoder_patch_entries
};
/*---------------------------------------------------------*\
| Keychron Q6 ANSI Encoder |
\*---------------------------------------------------------*/
#define KEYCHRON_Q6_ULTRA_8K_ANSI_PATCH_ENTRIES_COUNT (sizeof(keychron_q6_ultra_8k_ansi_patch_entries) / sizeof(keychron_patch_entry))
static const keychron_patch_entry keychron_q6_ultra_8k_ansi_patch_entries[] =
{
{ 19, 0, 20 },
{ 40, 1, 20 },
{ 77, 3, 20 },
{ 107, 5, 20 },
};
static const keychron_patch keychron_q6_ultra_8k_ansi_patch =
{
KEYCHRON_Q6_ULTRA_8K_ANSI_PID,
KEYCHRON_Q6_ULTRA_8K_ANSI_PATCH_ENTRIES_COUNT,
keychron_q6_ultra_8k_ansi_patch_entries
};
/*---------------------------------------------------------*\
| List of all Keychron patches |
\*---------------------------------------------------------*/
#define KEYCHRON_PATCH_COUNT (sizeof(keychron_patches) / sizeof(keychron_patch*))
static const keychron_patch* keychron_patches[] =
{
&keychron_q2_ansi_encoder_patch,
&keychron_q6_ultra_8k_ansi_patch,
&keychron_k5_v2_ansi_rgb_patch,
};
QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char *path, unsigned short dev_pid)
{
/*-----------------------------------------------------*\
| Initialize controller fields |
@@ -27,6 +135,7 @@ QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char
dev = dev_handle;
location = path;
kc_protocol_version = 0;
pid = dev_pid;
supported_features = 0;
via_protocol_version = 0;
@@ -143,6 +252,21 @@ QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char
{
keycodes[led_index] = CmdGetKeycode(0, led_info[led_index].row, led_info[led_index].col);
}
/*-----------------------------------------------------*\
| Apply matrix corrections |
\*-----------------------------------------------------*/
for(unsigned int patch_idx = 0; patch_idx < KEYCHRON_PATCH_COUNT; patch_idx++)
{
if(pid == keychron_patches[patch_idx]->pid)
{
for(unsigned int patch_entry_idx = 0; patch_entry_idx < keychron_patches[patch_idx]->num_entries; patch_entry_idx++)
{
led_info[keychron_patches[patch_idx]->patch[patch_entry_idx].led_idx].row = keychron_patches[patch_idx]->patch[patch_entry_idx].row;
led_info[keychron_patches[patch_idx]->patch[patch_entry_idx].led_idx].col = keychron_patches[patch_idx]->patch[patch_entry_idx].col;
}
}
}
}
QMKKeychronController::~QMKKeychronController()
@@ -114,6 +114,7 @@
#define KEYCHRON_K17_MAX_ISO_ENCODER_RGB_PID 0x0A01
#define KEYCHRON_K17_MAX_JIS_ENCODER_RGB_PID 0x0A02
#define KEYCHRON_Q0_BASE_PID 0x0130
#define KEYCHRON_Q0_HE_PID 0x0B00
#define KEYCHRON_Q0_PLUS_PID 0x0131
#define KEYCHRON_Q0_MAX_ENCODER_PID 0x0800
#define KEYCHRON_Q1_HE_ANSI_ENCODER_PID 0x0B10
@@ -389,7 +390,7 @@ enum KeychronVIABacklightValueID
class QMKKeychronController
{
public:
QMKKeychronController(hid_device* dev_handle, const char *path);
QMKKeychronController(hid_device* dev_handle, const char *path, unsigned short dev_pid);
~QMKKeychronController();
std::string GetLocation();
@@ -419,6 +420,7 @@ private:
std::string location;
std::string name;
unsigned short number_leds;
unsigned short pid;
std::string serial;
unsigned short supported_features;
std::string vendor;
@@ -23,7 +23,7 @@ DetectedControllers DetectQMKKeychronControllers(hid_device_info* info, const st
if(dev)
{
QMKKeychronController* controller = new QMKKeychronController(dev, info->path);
QMKKeychronController* controller = new QMKKeychronController(dev, info->path, info->product_id);
if(controller->GetSupported())
{
@@ -129,6 +129,7 @@ REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers
REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_ISO_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_JIS_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q0", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_BASE_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q0 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_HE_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q0 Plus", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_PLUS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q0 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_MAX_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
@@ -12,22 +12,18 @@
#include <hidapi.h>
#include "DetectionManager.h"
#include "RedSquareKeyroxController.h"
#include "RedSquareKeyroxTKLClassicController.h"
#include "RGBController_RedSquareKeyrox.h"
#include "RGBController_RedSquareKeyroxTKLClassic.h"
/*---------------------------------------------------------*\
| Red Square vendor ID |
\*---------------------------------------------------------*/
#define RED_SQUARE_VID 0x1A2C
#define RED_SQUARE_KEYROX_TKL_CLASSIC_VID 0x0416
/*---------------------------------------------------------*\
| Red Square product ID |
\*---------------------------------------------------------*/
#define RED_SQUARE_KEYROX_TKL_PID 0x1511
#define RED_SQUARE_KEYROX_TKL_V2_PID 0x2511
#define RED_SQUARE_KEYROX_TKL_CLASSIC_PID 0xC345
DetectedControllers DetectRedSquareKeyroxTKL(hid_device_info* info, const std::string& name)
{
@@ -47,24 +43,5 @@ DetectedControllers DetectRedSquareKeyroxTKL(hid_device_info* info, const std::s
return(detected_controllers);
}
DetectedControllers DetectRedSquareKeyroxTKLClassic(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
RedSquareKeyroxTKLClassicController* controller = new RedSquareKeyroxTKLClassicController(dev, *info, name);
RGBController_RedSquareKeyroxTKLClassic* rgb_controller = new RGBController_RedSquareKeyroxTKLClassic(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Red Square Keyrox TKL", DetectRedSquareKeyroxTKL, RED_SQUARE_VID, RED_SQUARE_KEYROX_TKL_PID, 3, 0xFF00, 2);
REGISTER_HID_DETECTOR_IPU("Red Square Keyrox TKL V2", DetectRedSquareKeyroxTKL, RED_SQUARE_VID, RED_SQUARE_KEYROX_TKL_V2_PID, 3, 0xFF00, 2);
REGISTER_HID_DETECTOR_I( "Red Square Keyrox TKL Classic", DetectRedSquareKeyroxTKLClassic, RED_SQUARE_KEYROX_TKL_CLASSIC_VID, RED_SQUARE_KEYROX_TKL_CLASSIC_PID, 2);
@@ -1,169 +0,0 @@
/*---------------------------------------------------------*\
| RedSquareKeyroxTKLClassicController.cpp |
| |
| Driver for Red Square Keyrox TKL Classic |
| |
| vlack 03 May 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "LogManager.h"
#include "RedSquareKeyroxTKLClassicController.h"
using namespace std::chrono_literals;
RedSquareKeyroxTKLClassicController::RedSquareKeyroxTKLClassicController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name)
{
dev = dev_handle;
location = info.path;
name = dev_name;
}
RedSquareKeyroxTKLClassicController::~RedSquareKeyroxTKLClassicController()
{
hid_close(dev);
}
std::string RedSquareKeyroxTKLClassicController::GetDeviceLocation()
{
return("HID: " + location);
}
std::string RedSquareKeyroxTKLClassicController::GetNameString()
{
return(name);
}
std::string RedSquareKeyroxTKLClassicController::GetSerialString()
{
wchar_t serial_wchar[128];
hid_get_serial_number_string(dev, serial_wchar, 128);
std::wstring serial_wstring(serial_wchar);
std::string serial_string;
std::transform(serial_wstring.begin(), serial_wstring.end(), std::back_inserter(serial_string), [] (wchar_t i)
{
return (char)i;
});
return serial_string;
}
int RedSquareKeyroxTKLClassicController::GetDirection(int direction)
{
switch(direction)
{
case MODE_DIRECTION_LEFT:
return 0x00;
case MODE_DIRECTION_RIGHT:
return 0x01;
case MODE_DIRECTION_UP:
return 0x02;
case MODE_DIRECTION_DOWN:
return 0x03;
case MODE_DIRECTION_HORIZONTAL: // actually direction out
return 0x04;
case MODE_DIRECTION_VERTICAL: // actually direction in
return 0x05;
default:
return 0x00;
}
}
int RedSquareKeyroxTKLClassicController::GetDirectionRound(int direction)
{
switch(direction)
{
case MODE_DIRECTION_LEFT: // actually anticlockwise
return 0x07;
case MODE_DIRECTION_RIGHT: // actually clockwise
return 0x06;
default:
return 0x00;
}
}
void RedSquareKeyroxTKLClassicController::SetMode(mode m)
{
/*---------------------------------------------*\
| Mode set command |
\*---------------------------------------------*/
unsigned char usb_buf[CLASSIC_PACKET_DATA_LENGTH];
memset(usb_buf, 0x00, CLASSIC_PACKET_DATA_LENGTH);
usb_buf[0] = 0x01;
usb_buf[1] = 0x07;
usb_buf[6] = m.value;
usb_buf[7] = m.brightness; // brightness
usb_buf[8] = m.speed; // speed
if(m.colors_max == 1 || m.colors_max == 2)
{
usb_buf[9] = RGBGetRValue(m.colors[0]); // front R
usb_buf[10] = RGBGetGValue(m.colors[0]); // front G
usb_buf[11] = RGBGetBValue(m.colors[0]); // front B
}
if(m.colors_max == 2 && m.color_mode != MODE_COLORS_RANDOM)
{
usb_buf[12] = RGBGetRValue(m.colors[1]); // back R
usb_buf[13] = RGBGetGValue(m.colors[1]); // back G
usb_buf[14] = RGBGetBValue(m.colors[1]); // back B
}
if(m.value == CLASSIC_RADAR_MODE_VALUE || m.value == CLASSIC_RUNNING_LINE_MODE_VALUE)
{
usb_buf[15] = GetDirectionRound(m.direction);
}
else
{
usb_buf[15] = GetDirection(m.direction);
}
usb_buf[16] = m.color_mode == MODE_COLORS_RANDOM;
hid_write(dev, usb_buf, CLASSIC_PACKET_DATA_LENGTH);
// sleep is necessary
std::this_thread::sleep_for(10ms);
}
void RedSquareKeyroxTKLClassicController::SetLEDsData(std::vector<RGBColor> colors, std::vector<led> leds)
{
/*---------------------------------------------*\
| LEDs data set command |
\*---------------------------------------------*/
unsigned char usb_buf[CLASSIC_PACKET_DATA_LENGTH * 8];
memset(usb_buf, 0x00, CLASSIC_PACKET_DATA_LENGTH * 8);
for(unsigned int i = 0; i < colors.size(); i++)
{
int offset = leds[i].value;
usb_buf[offset - 1] = RGBGetRValue(colors[i]);
usb_buf[offset] = RGBGetGValue(colors[i]);
usb_buf[offset + 1] = RGBGetBValue(colors[i]);
}
for(unsigned int i = 0; i < 8; i++)
{
unsigned char packet[CLASSIC_PACKET_DATA_LENGTH];
memset(packet, 0x00, CLASSIC_PACKET_DATA_LENGTH);
packet[0] = 0x01;
packet[1] = 0x0F;
packet[4] = i; // package number
packet[5] = i == 7 ? 0x12 : 0x36; // package size
for (int x = 6; x < CLASSIC_PACKET_DATA_LENGTH; x++)
{
packet[x] = usb_buf[CLASSIC_PACKET_DATA_LENGTH * i + x];
}
hid_write(dev, packet, CLASSIC_PACKET_DATA_LENGTH);
}
// sleep is necessary
std::this_thread::sleep_for(10ms);
}
@@ -1,78 +0,0 @@
/*---------------------------------------------------------*\
| RedSquareKeyroxTKLClassicController.h |
| |
| Driver for Red Square Keyrox TKL Classic |
| |
| vlack 03 May 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#define CLASSIC_PACKET_DATA_LENGTH 64
/*---------------------------------------*\
| Modes |
\*---------------------------------------*/
enum
{
CLASSIC_CONST_MODE_VALUE = 0x00, // static
CLASSIC_BREATHE_MODE_VALUE = 0x01, // breath
CLASSIC_WAVE_MODE_VALUE = 0x02, // wave
CLASSIC_FADE_MODE_VALUE = 0x03, // neon
CLASSIC_RADAR_MODE_VALUE = 0x04, // radar
CLASSIC_STAR_MODE_VALUE = 0x06, // интерактив
CLASSIC_LINE_MODE_VALUE = 0x07, // сияние
CLASSIC_RIPPLE_MODE_VALUE = 0x08, // рябь интерактив
CLASSIC_STARS_MODE_VALUE = 0x09, // мерцание
CLASSIC_CUSTOM_MODE_VALUE = 0x0A,
CLASSIC_CROSS_MODE_VALUE = 0x0B, // скрещивание
CLASSIC_WTF_MODE_VALUE = 0x0C, // быстрый отклик
CLASSIC_RIPPLE_RANDOM_MODE_VALUE = 0x0E, // рябь
CLASSIC_RUNNING_LINE_MODE_VALUE = 0x0F, // бегущая строка
CLASSIC_FIREWORK_MODE_VALUE = 0x10, // firework
};
/*-----------------------------*\
| Other settings |
\*-----------------------------*/
enum
{
CLASSIC_KEYROX_BRIGHTNESS_MIN = 0x00,
CLASSIC_KEYROX_BRIGHTNESS_MAX = 0x04,
CLASSIC_KEYROX_SPEED_MIN = 0x00,
CLASSIC_KEYROX_SPEED_MAX = 0x04,
};
class RedSquareKeyroxTKLClassicController
{
public:
RedSquareKeyroxTKLClassicController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name);
~RedSquareKeyroxTKLClassicController();
std::string GetDeviceLocation();
std::string GetNameString();
std::string GetSerialString();
int GetDirection(int direction);
int GetDirectionRound(int direction);
void SetMode(mode m);
void SetLEDsData(std::vector<RGBColor> colors, std::vector<led> leds);
protected:
hid_device* dev;
private:
std::string location;
std::string name;
std::string serial_number;
std::vector<unsigned int> led_sequence_positions;
};
@@ -151,6 +151,9 @@ void RGBController_RoccatVulcanKeyboard::SetupZones()
case ROCCAT_VULCAN_LAYOUT_FR:
layout = ROCCAT_VULCAN_LAYOUT_UK;
break;
case ROCCAT_VULCAN_LAYOUT_JP:
layout = ROCCAT_VULCAN_LAYOUT_JP;
break;
case ROCCAT_VULCAN_LAYOUT_US:
default:
layout = ROCCAT_VULCAN_LAYOUT_US;
@@ -167,7 +170,13 @@ void RGBController_RoccatVulcanKeyboard::SetupZones()
if(pid == ROCCAT_VULCAN_II_MAX_PID)
{
keyboard_size = 108;
/*---------------------------------------------------------*\
| Dynamically calculate the main keyboard zone size by |
| subtracting the 40 secondary/palm rest LEDs from the |
| total layout size. This prevents JIS keys from overflow. |
\*---------------------------------------------------------*/
keyboard_size = keyboard[layout].size - 40;
}
zone keyboard_zone;
@@ -84,6 +84,10 @@ device_info RoccatVulcanKeyboardController::InitDeviceInfo()
memset(buf, 0x00, packet_length);
buf[0] = report_id;
/*-------------------------------------------------------------*\
| Store the actual read bytes to verify data availability |
\*-------------------------------------------------------------*/
int res = hid_get_feature_report(dev_ctrl, buf, packet_length);
hid_get_feature_report(dev_ctrl, buf, packet_length);
/*-------------------------------------------------------------*\
@@ -107,11 +111,22 @@ device_info RoccatVulcanKeyboardController::InitDeviceInfo()
}
else
{
dev_info.layout_type = buf[6];
/*---------------------------------------------------------*\
| Safe check for reported buffer length before accessing |
| buf[6] to prevent out-of-bounds array access. |
| Vulcan II Max (JP) returns 0x06 at buf[6] for JIS layout. |
\*---------------------------------------------------------*/
if(res >= 7)
{
dev_info.layout_type = buf[6];
LOG_DEBUG("[Roccat Vulcan Keyboard]: Detected layout '0x%02X'", buf[6]);
}
else
{
dev_info.layout_type = ROCCAT_VULCAN_LAYOUT_US;
LOG_DEBUG("[Roccat Vulcan Keyboard]: Failed to detect layout, falling back to US");
}
}
LOG_DEBUG("[Roccat Vulcan Keyboard]: Detected layout '0x%02X'", buf[6]);
delete[] buf;
return dev_info;
}
@@ -22,6 +22,7 @@ enum
ROCCAT_VULCAN_LAYOUT_DE = 1,
ROCCAT_VULCAN_LAYOUT_UK = 2,
ROCCAT_VULCAN_LAYOUT_FR = 3,
ROCCAT_VULCAN_LAYOUT_JP = 6, /* Updated based on Vulcan II Max JIS firmware report value */
};
#define NA 0xFFFFFFFF
@@ -815,6 +816,16 @@ static unsigned int ROCCAT_VULCAN_II_MAX_LAYOUT_KEYS_ANSI[6][24] =
{ 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, 74, NA, NA, 78, 83, 87, NA, 92, NA, 101, NA }
};
static unsigned int ROCCAT_VULCAN_II_MAX_LAYOUT_KEYS_JIS[6][24] =
{
{ 0, NA, 7, 13, 19, 25, NA, 35, 40, 45, 51, 57, 63, 69, 73, NA, 78, 82, 87, NA, 108, 109, 110, NA },
{ 1, 6, 8, 14, 20, 26, 30, 36, 41, 46, 52, 58, 64, 70, 74, NA, 79, 83, 88, NA, 91, 96, 100, 105 },
{ 2, NA, 9, 15, 21, 27, 31, 37, 42, 47, 53, 59, 65, 71, 75, NA, 80, 84, 89, NA, 92, 97, 101, 106 },
{ 3, NA, 10, 16, 22, 28, 32, 38, 43, 48, 54, 60, 66, 72, NA, NA, NA, NA, NA, NA, 93, 98, 102, NA },
{ 4, NA, 11, 17, 23, 29, 33, 39, 44, 49, 55, 61, 67, NA, 76, NA, NA, 85, NA, NA, 94, 99, 103, 107 },
{ 5, NA, 12, 18, 24, NA, 34, NA, NA, 50, 56, 62, 68, NA, 77, NA, 81, 86, 90, NA, 95, NA, 104, NA }
};
static std::map<int, layout_info> RoccatVulcanIIMaxLayouts =
{
{
@@ -1005,7 +1016,219 @@ static std::map<int, layout_info> RoccatVulcanIIMaxLayouts =
{ "Key: Left Windows LED 2", 0x83 },
/*-------------------------------------------------------------*\
| Palm Rest LEDs (order approximate - needs verification) |
| Palm Rest LEDs (order approximate - needs verification) |
\*-------------------------------------------------------------*/
{ "Palm Rest LED 1", 0x96 },
{ "Palm Rest LED 2", 0xAD },
{ "Palm Rest LED 3", 0xA7 },
{ "Palm Rest LED 4", 0xA1 },
{ "Palm Rest LED 5", 0x9B },
{ "Palm Rest LED 6", 0x95 },
{ "Palm Rest LED 7", 0xAC },
{ "Palm Rest LED 8", 0xA6 },
{ "Palm Rest LED 9", 0xA0 },
{ "Palm Rest LED 10", 0x9A },
{ "Palm Rest LED 11", 0x94 },
{ "Palm Rest LED 12", 0xAB },
{ "Palm Rest LED 13", 0xA5 },
{ "Palm Rest LED 14", 0x9F },
{ "Palm Rest LED 15", 0x99 },
{ "Palm Rest LED 16", 0x93 }
}
}
},
{
ROCCAT_VULCAN_LAYOUT_JP,
{
*ROCCAT_VULCAN_II_MAX_LAYOUT_KEYS_JIS,
151,
6,
24,
{
{ KEY_EN_ESCAPE, 0x02 },
{ KEY_JP_EJ, 0x03 },
{ KEY_EN_TAB, 0x04 },
{ KEY_EN_CAPS_LOCK, 0x05 },
{ KEY_EN_LEFT_SHIFT, 0x00 },
{ KEY_EN_LEFT_CONTROL, 0x01 },
{ KEY_EN_1, 0x08 },
{ KEY_EN_F1, 0x0D },
{ KEY_EN_2, 0x0E },
{ KEY_EN_Q, 0x09 },
{ KEY_EN_A, 0x0A },
{ KEY_EN_Z, 0x0B },
{ KEY_EN_LEFT_WINDOWS, 0x07 },
{ KEY_EN_F2, 0x14 },
{ KEY_EN_3, 0x15 },
{ KEY_EN_W, 0x0F },
{ KEY_EN_S, 0x10 },
{ KEY_EN_X, 0x11 },
{ KEY_EN_LEFT_ALT, 0x0C },
{ KEY_EN_F3, 0x19 },
{ KEY_EN_4, 0x1A },
{ KEY_EN_E, 0x16 },
{ KEY_EN_D, 0x17 },
{ KEY_EN_C, 0x18 },
{ KEY_JP_MUHENKAN, 0x12 },
{ KEY_EN_F4, 0x1F },
{ KEY_EN_5, 0x1E },
{ KEY_EN_R, 0x1B },
{ KEY_EN_F, 0x1C },
{ KEY_EN_V, 0x21 },
{ KEY_EN_6, 0x24 },
{ KEY_EN_T, 0x20 },
{ KEY_EN_G, 0x1D },
{ KEY_EN_B, 0x22 },
{ KEY_EN_SPACE, 0x28 },
{ KEY_EN_F5, 0x23 },
{ KEY_EN_7, 0x29 },
{ KEY_EN_Y, 0x25 },
{ KEY_EN_H, 0x26 },
{ KEY_EN_N, 0x27 },
{ KEY_EN_F6, 0x2F },
{ KEY_EN_8, 0x30 },
{ KEY_EN_U, 0x3D },
{ KEY_EN_J, 0x2B },
{ KEY_EN_M, 0x2C },
{ KEY_EN_F7, 0x35 },
{ KEY_EN_9, 0x36 },
{ KEY_EN_I, 0x31 },
{ KEY_EN_K, 0x32 },
{ KEY_EN_COMMA, 0x33 },
{ KEY_JP_HENKAN, 0x2D },
{ KEY_EN_F8, 0x3B },
{ KEY_EN_0, 0x3C },
{ KEY_EN_O, 0x37 },
{ KEY_EN_L, 0x38 },
{ KEY_EN_PERIOD, 0x39 },
{ KEY_JP_KANA, 0x34 },
{ KEY_EN_F9, 0x41 },
{ KEY_EN_MINUS, 0x42 },
{ KEY_EN_P, 0x2A },
{ KEY_EN_SEMICOLON, 0x3E },
{ KEY_EN_FORWARD_SLASH, 0x3F },
{ KEY_EN_RIGHT_FUNCTION, 0x40 },
{ KEY_EN_F10, 0x47 },
{ KEY_JP_CHEVRON, 0x48 },
{ KEY_JP_AT, 0x43 },
{ KEY_JP_COLON, 0x44 },
{ KEY_EN_BACK_SLASH, 0x45 },
{ KEY_EN_MENU, 0x46 },
{ KEY_EN_F11, 0x4D },
{ KEY_JP_YEN, 0x4E },
{ KEY_EN_LEFT_BRACKET, 0x49 },
{ KEY_EN_RIGHT_BRACKET, 0x4A },
{ KEY_EN_F12, 0x4F },
{ KEY_EN_BACKSPACE, 0x50 },
{ KEY_EN_ISO_ENTER, 0x52 },
{ KEY_EN_RIGHT_SHIFT, 0x4B },
{ KEY_EN_RIGHT_CONTROL, 0x4C },
{ KEY_EN_PRINT_SCREEN, 0x53 },
{ KEY_EN_INSERT, 0x54 },
{ KEY_EN_DELETE, 0x55 },
{ KEY_EN_LEFT_ARROW, 0x56 },
{ KEY_EN_SCROLL_LOCK, 0x57 },
{ KEY_EN_HOME, 0x58 },
{ KEY_EN_END, 0x59 },
{ KEY_EN_UP_ARROW, 0x5A },
{ KEY_EN_DOWN_ARROW, 0x5B },
{ KEY_EN_PAUSE_BREAK, 0x5C },
{ KEY_EN_PAGE_UP, 0x5D },
{ KEY_EN_PAGE_DOWN, 0x5E },
{ KEY_EN_RIGHT_ARROW, 0x5F },
{ KEY_EN_NUMPAD_LOCK, 0x60 },
{ KEY_EN_NUMPAD_7, 0x61 },
{ KEY_EN_NUMPAD_4, 0x62 },
{ KEY_EN_NUMPAD_1, 0x6C },
{ KEY_EN_NUMPAD_0, 0x64 },
{ KEY_EN_NUMPAD_DIVIDE, 0x65 },
{ KEY_EN_NUMPAD_8, 0x66 },
{ KEY_EN_NUMPAD_5, 0x67 },
{ KEY_EN_NUMPAD_2, 0x68 },
{ KEY_EN_NUMPAD_TIMES, 0x69 },
{ KEY_EN_NUMPAD_9, 0x6A },
{ KEY_EN_NUMPAD_6, 0x6B },
{ KEY_EN_NUMPAD_3, 0x63 },
{ KEY_EN_NUMPAD_PERIOD, 0x6D },
{ KEY_EN_NUMPAD_MINUS, 0x6E },
{ KEY_EN_NUMPAD_PLUS, 0x6F },
{ KEY_EN_NUMPAD_ENTER, 0x71 },
{ KEY_EN_MEDIA_PREVIOUS, 0x72 },
{ KEY_EN_MEDIA_PLAY_PAUSE, 0x73 },
{ KEY_EN_MEDIA_NEXT, 0x74 },
/*-------------------------------------------------------------*\
| F-Key Indicators (secondary LEDs) |
\*-------------------------------------------------------------*/
{ "Key: F1 LED 2", 0x89 },
{ "Key: F2 LED 2", 0x7B },
{ "Key: F3 LED 2", 0x81 },
{ "Key: F4 LED 2", 0x87 },
{ "Key: F5 LED 2", 0x8D },
{ "Key: F6 LED 2", 0x76 },
{ "Key: F7 LED 2", 0x7C },
{ "Key: F8 LED 2", 0x82 },
{ "Key: F9 LED 2", 0x88 },
{ "Key: F10 LED 2", 0x8E },
{ "Key: F11 LED 2", 0x7A },
{ "Key: F12 LED 2", 0x80 },
{ "Key: Print Screen LED 2", 0x86 },
{ "Key: Scroll Lock LED 2", 0x8C },
{ "Key: Pause/Break LED 2", 0x92 },
/*-------------------------------------------------------------*\
| Nav Cluster Indicators (secondary LEDs) |
\*-------------------------------------------------------------*/
{ "Key: Insert LED 2", 0x75 },
{ "Key: Home LED 2", 0x7E },
{ "Key: Page Up LED 2", 0x84 },
/*-------------------------------------------------------------*\
| Num Lock Indicator (secondary LED) |
\*-------------------------------------------------------------*/
{ "Key: Num Lock LED 2", 0x8B },
/*-------------------------------------------------------------*\
| Nav Cluster Indicators 2 (secondary LEDs) |
\*-------------------------------------------------------------*/
{ "Key: Delete LED 2", 0x78 },
{ "Key: End LED 2", 0x90 },
{ "Key: Page Down LED 2", 0x8A },
/*-------------------------------------------------------------*\
| Caps Lock Indicator (secondary LED) |
\*-------------------------------------------------------------*/
{ "Key: Caps Lock LED 2", 0x7D },
/*-------------------------------------------------------------*\
| Win Key Indicator (secondary LED) |
\*-------------------------------------------------------------*/
{ "Key: Left Windows LED 2", 0x83 },
/*-------------------------------------------------------------*\
| Palm Rest LEDs (order approximate - needs verification) |
\*-------------------------------------------------------------*/
{ "Palm Rest LED 1", 0x96 },
{ "Palm Rest LED 2", 0xAD },
@@ -0,0 +1,118 @@
/*---------------------------------------------------------*\
| RGBController_SkydimoHID.cpp |
| |
| RGBController for Skydimo HID devices |
| |
| Bartholomew Ho <qiangqiang101@hotmail.com> 09 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_SkydimoHID.h"
/**------------------------------------------------------------------*\
@name Skydimo SK0902
@category HeadsetStand
@type USB
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectSkydimoHIDControllers
@comment
\*-------------------------------------------------------------------*/
static const unsigned int skydimo_hid_led_positions[SKYDIMO_HID_LED_COUNT][2] =
{
{0, 6}, {0, 5}, {0, 4}, {0, 3}, {0, 2}, {0, 1}, {0, 0},
{1, 0}, {1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6},
{2, 6}, {2, 5}, {2, 4}, {2, 3}, {2, 2}, {2, 1}, {2, 0},
{3, 0}, {3, 1}, {3, 2}, {3, 3}, {3, 4}, {3, 5}, {3, 6},
{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0},
{5, 0}, {5, 1}, {5, 2}, {5, 3}, {5, 4}, {5, 5}, {5, 6},
{6, 6}, {6, 5}, {6, 4}, {6, 3}, {6, 2}, {6, 1}, {6, 0}
};
RGBController_SkydimoHID::RGBController_SkydimoHID(SkydimoHIDController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetDeviceName();
vendor = "Skydimo";
type = DEVICE_TYPE_HEADSET_STAND;
description = "Skydimo HID Device";
serial = controller->GetSerial();
location = controller->GetLocation();
mode direct_mode;
direct_mode.name = "Direct";
direct_mode.value = 0;
direct_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR;
direct_mode.color_mode = MODE_COLORS_PER_LED;
modes.push_back(direct_mode);
SetupZones();
}
RGBController_SkydimoHID::~RGBController_SkydimoHID()
{
Shutdown();
delete controller;
}
void RGBController_SkydimoHID::SetupZones()
{
zone led_zone;
led_zone.name = "Matrix";
led_zone.type = ZONE_TYPE_MATRIX;
led_zone.leds_min = SKYDIMO_HID_LED_COUNT;
led_zone.leds_max = SKYDIMO_HID_LED_COUNT;
led_zone.leds_count = SKYDIMO_HID_LED_COUNT;
led_zone.matrix_map.height = SKYDIMO_HID_MATRIX_SIZE;
led_zone.matrix_map.width = SKYDIMO_HID_MATRIX_SIZE;
led_zone.matrix_map.map.assign(SKYDIMO_HID_MATRIX_SIZE * SKYDIMO_HID_MATRIX_SIZE, SKYDIMO_HID_MATRIX_NA);
/*-----------------------------------------------------*\
| Construct the matrix map |
\*-----------------------------------------------------*/
for(unsigned int led_idx = 0; led_idx < SKYDIMO_HID_LED_COUNT; led_idx++)
{
unsigned int column = skydimo_hid_led_positions[led_idx][0];
unsigned int row = skydimo_hid_led_positions[led_idx][1];
led_zone.matrix_map.map[(row * SKYDIMO_HID_MATRIX_SIZE) + column] = led_idx;
}
zones.push_back(led_zone);
for(unsigned int led_idx = 0; led_idx < SKYDIMO_HID_LED_COUNT; led_idx++)
{
led new_led;
new_led.name = "LED " + std::to_string(led_idx + 1);
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_SkydimoHID::DeviceUpdateLEDs()
{
controller->SetLEDs(colors);
}
void RGBController_SkydimoHID::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_SkydimoHID::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_SkydimoHID::DeviceUpdateMode()
{
}
@@ -0,0 +1,32 @@
/*---------------------------------------------------------*\
| RGBController_SkydimoHID.h |
| |
| RGBController for Skydimo HID devices |
| |
| Bartholomew Ho <qiangqiang101@hotmail.com> 09 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "SkydimoHIDController.h"
class RGBController_SkydimoHID : public RGBController
{
public:
RGBController_SkydimoHID(SkydimoHIDController* controller_ptr);
~RGBController_SkydimoHID();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
private:
SkydimoHIDController* controller;
};
@@ -0,0 +1,195 @@
/*---------------------------------------------------------*\
| SkydimoHIDController.cpp |
| |
| Driver for Skydimo HID devices |
| |
| Bartholomew Ho <qiangqiang101@hotmail.com> 09 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include "LogManager.h"
#include "SkydimoHIDController.h"
#include "StringUtils.h"
static unsigned char CalculateCRC8(const unsigned char* data, unsigned int length)
{
unsigned char crc = 0x00;
for(unsigned int byte_idx = 0; byte_idx < length; byte_idx++)
{
crc ^= data[byte_idx];
for(unsigned int bit_idx = 0; bit_idx < SKYDIMO_HID_CRC_BIT_COUNT; bit_idx++)
{
if(crc & SKYDIMO_HID_CRC_HIGH_BIT)
{
crc = (unsigned char)((crc << 1) ^ SKYDIMO_HID_CRC_POLYNOMIAL);
}
else
{
crc = (unsigned char)(crc << 1);
}
}
}
return(crc);
}
SkydimoHIDController::SkydimoHIDController(hid_device* dev_handle, const char* path)
{
dev = dev_handle;
location = path;
wchar_t name_string[SKYDIMO_HID_NAME_BUFFER_SIZE];
int result;
result = hid_get_manufacturer_string(dev, name_string, SKYDIMO_HID_NAME_BUFFER_SIZE);
if(result == 0)
{
device_name = StringUtils::wstring_to_string(name_string);
}
else
{
device_name = "Skydimo";
LOG_WARNING("[SkydimoHIDController] Failed to read manufacturer string for %s", path);
}
result = hid_get_product_string(dev, name_string, SKYDIMO_HID_NAME_BUFFER_SIZE);
if(result == 0)
{
device_name.append(" ").append(StringUtils::wstring_to_string(name_string));
}
else
{
device_name.append(" HID Device");
LOG_WARNING("[SkydimoHIDController] Failed to read product string for %s", path);
}
wchar_t serial_string[SKYDIMO_HID_SERIAL_BUFFER_SIZE];
result = hid_get_serial_number_string(dev, serial_string, SKYDIMO_HID_SERIAL_BUFFER_SIZE);
if(result == 0)
{
serial = StringUtils::wstring_to_string(serial_string);
}
else
{
serial = "";
LOG_DEBUG("[SkydimoHIDController] No serial number available for %s", path);
}
}
SkydimoHIDController::~SkydimoHIDController()
{
if(dev)
{
hid_close(dev);
}
}
std::string SkydimoHIDController::GetDeviceName() const
{
return(device_name);
}
std::string SkydimoHIDController::GetSerial() const
{
return(serial);
}
std::string SkydimoHIDController::GetLocation() const
{
return("HID: " + location);
}
bool SkydimoHIDController::SendPacket(unsigned char start_led, const unsigned char* chunk, unsigned int chunk_length)
{
unsigned char packet[SKYDIMO_HID_PACKET_SIZE];
memset(packet, 0x00, sizeof(packet));
packet[0] = SKYDIMO_HID_REPORT_ID;
packet[SKYDIMO_HID_PACKET_START_LED_INDEX] = start_led;
packet[SKYDIMO_HID_PACKET_RESERVED_INDEX] = 0x00;
if(chunk_length > SKYDIMO_HID_PACKET_DATA_SIZE)
{
chunk_length = SKYDIMO_HID_PACKET_DATA_SIZE;
}
memcpy(&packet[SKYDIMO_HID_PACKET_DATA_OFFSET], chunk, chunk_length);
/*-----------------------------------------------------*\
| Calculate CRC8 over all bytes before the checksum |
\*-----------------------------------------------------*/
packet[SKYDIMO_HID_PACKET_SIZE - 1] = CalculateCRC8(packet, SKYDIMO_HID_PACKET_SIZE - 1);
int bytes_written = hid_write(dev, packet, SKYDIMO_HID_PACKET_SIZE);
if(bytes_written != SKYDIMO_HID_PACKET_SIZE)
{
LOG_ERROR("[SkydimoHIDController] Failed to write RGB packet at LED %u (%d/%u bytes): %ls",
(unsigned int)start_led, bytes_written, (unsigned int)SKYDIMO_HID_PACKET_SIZE, hid_error(dev));
return(false);
}
return(true);
}
void SkydimoHIDController::SetLEDs(const std::vector<RGBColor>& colors)
{
std::lock_guard<std::mutex> guard(device_mutex);
unsigned char frame_data[SKYDIMO_HID_FRAME_SIZE];
memset(frame_data, 0, sizeof(frame_data));
/*-----------------------------------------------------*\
| Convert colors to GRB format |
\*-----------------------------------------------------*/
for(unsigned int led_idx = 0; (led_idx < colors.size()) && (led_idx < SKYDIMO_HID_LED_COUNT); led_idx++)
{
unsigned int color_offset = led_idx * SKYDIMO_HID_BYTES_PER_LED;
frame_data[color_offset + 0] = RGBGetGValue(colors[led_idx]);
frame_data[color_offset + 1] = RGBGetRValue(colors[led_idx]);
frame_data[color_offset + 2] = RGBGetBValue(colors[led_idx]);
}
/*-----------------------------------------------------*\
| Send the frame in packet-sized chunks |
\*-----------------------------------------------------*/
for(unsigned int frame_offset = 0; frame_offset < SKYDIMO_HID_FRAME_SIZE; frame_offset += SKYDIMO_HID_PACKET_DATA_SIZE)
{
unsigned int bytes_remaining = SKYDIMO_HID_FRAME_SIZE - frame_offset;
unsigned int chunk_length = (bytes_remaining < SKYDIMO_HID_PACKET_DATA_SIZE)
? bytes_remaining
: SKYDIMO_HID_PACKET_DATA_SIZE;
unsigned char start_led = (unsigned char)(frame_offset / SKYDIMO_HID_BYTES_PER_LED);
if(!SendPacket(start_led, &frame_data[frame_offset], chunk_length))
{
return;
}
}
/*-----------------------------------------------------*\
| Send the frame-end packet |
\*-----------------------------------------------------*/
unsigned char end_packet[SKYDIMO_HID_PACKET_SIZE];
memset(end_packet, 0x00, sizeof(end_packet));
end_packet[0] = SKYDIMO_HID_REPORT_ID;
end_packet[SKYDIMO_HID_END_COMMAND_INDEX] = SKYDIMO_HID_END_COMMAND_VALUE;
end_packet[SKYDIMO_HID_END_SUBCOMMAND_INDEX] = SKYDIMO_HID_END_SUBCOMMAND_VALUE;
end_packet[SKYDIMO_HID_END_LED_COUNT_INDEX] = SKYDIMO_HID_LED_COUNT;
end_packet[SKYDIMO_HID_END_MARKER_INDEX] = SKYDIMO_HID_END_MARKER_VALUE;
int bytes_written = hid_write(dev, end_packet, SKYDIMO_HID_PACKET_SIZE);
if(bytes_written != SKYDIMO_HID_PACKET_SIZE)
{
LOG_ERROR("[SkydimoHIDController] Failed to write frame-end packet (%d/%u bytes): %ls",
bytes_written, (unsigned int)SKYDIMO_HID_PACKET_SIZE, hid_error(dev));
}
}
@@ -0,0 +1,64 @@
/*---------------------------------------------------------*\
| SkydimoHIDController.h |
| |
| Driver for Skydimo HID devices |
| |
| Bartholomew Ho <qiangqiang101@hotmail.com> 09 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <mutex>
#include <string>
#include <vector>
#include <hidapi.h>
#include "RGBControllerInterface.h"
#define SKYDIMO_HID_LED_COUNT 49
#define SKYDIMO_HID_MATRIX_SIZE 7
#define SKYDIMO_HID_BYTES_PER_LED 3
#define SKYDIMO_HID_FRAME_SIZE (SKYDIMO_HID_LED_COUNT * SKYDIMO_HID_BYTES_PER_LED)
#define SKYDIMO_HID_PACKET_SIZE 64
#define SKYDIMO_HID_PACKET_DATA_SIZE 60
#define SKYDIMO_HID_PACKET_DATA_OFFSET 3
#define SKYDIMO_HID_REPORT_ID 0x01
#define SKYDIMO_HID_PACKET_START_LED_INDEX 1
#define SKYDIMO_HID_PACKET_RESERVED_INDEX 2
#define SKYDIMO_HID_MATRIX_NA 0xFFFFFFFF
#define SKYDIMO_HID_CRC_POLYNOMIAL 0x07
#define SKYDIMO_HID_CRC_HIGH_BIT 0x80
#define SKYDIMO_HID_CRC_BIT_COUNT 8
#define SKYDIMO_HID_NAME_BUFFER_SIZE 255
#define SKYDIMO_HID_SERIAL_BUFFER_SIZE 128
#define SKYDIMO_HID_END_MARKER_INDEX 60
#define SKYDIMO_HID_END_MARKER_VALUE 0x1E
#define SKYDIMO_HID_END_COMMAND_INDEX 1
#define SKYDIMO_HID_END_COMMAND_VALUE 0xFF
#define SKYDIMO_HID_END_SUBCOMMAND_INDEX 2
#define SKYDIMO_HID_END_SUBCOMMAND_VALUE 0xFF
#define SKYDIMO_HID_END_LED_COUNT_INDEX 3
class SkydimoHIDController
{
public:
SkydimoHIDController(hid_device* dev_handle, const char* path);
~SkydimoHIDController();
std::string GetDeviceName() const;
std::string GetSerial() const;
std::string GetLocation() const;
void SetLEDs(const std::vector<RGBColor>& colors);
private:
hid_device* dev;
std::string device_name;
std::string serial;
std::string location;
std::mutex device_mutex;
bool SendPacket(unsigned char start_led, const unsigned char* chunk, unsigned int chunk_length);
};
@@ -0,0 +1,50 @@
/*---------------------------------------------------------*\
| SkydimoHIDControllerDetect.cpp |
| |
| Detector for Skydimo HID devices |
| |
| Bartholomew Ho <qiangqiang101@hotmail.com> 09 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "LogManager.h"
#include "RGBController_SkydimoHID.h"
#include "SkydimoHIDController.h"
/*---------------------------------------------------------*\
| Skydimo HID vendor and product IDs |
\*---------------------------------------------------------*/
#define SKYDIMO_VID 0x1A86
#define SKYDIMO_SK0902_PID 0xE316
#define SKYDIMO_SK0902_INTERFACE 0
#define SKYDIMO_SK0902_USAGE_PAGE 0xFF00
#define SKYDIMO_SK0902_USAGE 0x01
DetectedControllers DetectSkydimoHIDControllers(hid_device_info* info, const std::string&)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
SkydimoHIDController* controller = new SkydimoHIDController(dev, info->path);
RGBController_SkydimoHID* rgb_controller = new RGBController_SkydimoHID(controller);
detected_controllers.push_back(rgb_controller);
LOG_INFO("[SkydimoHIDControllerDetect] Detected Skydimo SK0902 at %s", info->path);
}
else
{
LOG_WARNING("[SkydimoHIDControllerDetect] Failed to open Skydimo SK0902 at %s", info->path);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Skydimo SK0902", DetectSkydimoHIDControllers, SKYDIMO_VID, SKYDIMO_SK0902_PID, SKYDIMO_SK0902_INTERFACE, SKYDIMO_SK0902_USAGE_PAGE, SKYDIMO_SK0902_USAGE);
@@ -0,0 +1,147 @@
/*---------------------------------------------------------*\
| RGBController_SkydimoSerial.cpp |
| |
| RGBController for Skydimo serial devices |
| |
| Bartholomew Ho <qiangqiang101@hotmail.com> 30 Jun 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_SkydimoSerial.h"
/**------------------------------------------------------------------*\
@name Skydimo Serial
@category LEDStrip
@type Serial
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectSkydimoSerialControllers
@comment
\*-------------------------------------------------------------------*/
RGBController_SkydimoSerial::RGBController_SkydimoSerial(SkydimoSerialController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetName();
vendor = "Skydimo";
type = DEVICE_TYPE_LEDSTRIP;
description = "Skydimo Serial Device";
serial = controller->GetSerial();
location = controller->GetLocation();
mode direct_mode;
direct_mode.name = "Direct";
direct_mode.value = 0;
direct_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR;
direct_mode.color_mode = MODE_COLORS_PER_LED;
modes.push_back(direct_mode);
SetupZones();
}
RGBController_SkydimoSerial::~RGBController_SkydimoSerial()
{
Shutdown();
controller->SetBlack();
delete controller;
}
void RGBController_SkydimoSerial::SetupZones()
{
bool first_run = zones.empty();
leds.clear();
colors.clear();
zones.resize(1);
zones[0].leds_min = controller->GetMinLEDCount();
zones[0].leds_max = controller->GetMaxLEDCount();
if(first_run)
{
if(controller->IsResizable())
{
zones[0].flags = ZONE_FLAG_MANUALLY_CONFIGURABLE_SIZE
| ZONE_FLAG_MANUALLY_CONFIGURABLE_NAME
| ZONE_FLAG_MANUALLY_CONFIGURABLE_TYPE
| ZONE_FLAG_MANUALLY_CONFIGURABLE_MATRIX_MAP
| ZONE_FLAG_MANUALLY_CONFIGURABLE_SEGMENTS;
}
}
if(!(zones[0].flags & ZONE_FLAG_MANUALLY_CONFIGURED_NAME))
{
zones[0].name = "Output 1";
}
if(!(zones[0].flags & ZONE_FLAG_MANUALLY_CONFIGURED_SIZE))
{
zones[0].leds_count = controller->GetLEDCount();
}
if(!(zones[0].flags & ZONE_FLAG_MANUALLY_CONFIGURED_TYPE))
{
zones[0].type = controller->GetMatrixMap().empty() ? ZONE_TYPE_LINEAR : ZONE_TYPE_MATRIX;
}
if(!(zones[0].flags & ZONE_FLAG_MANUALLY_CONFIGURED_MATRIX_MAP))
{
zones[0].matrix_map.height = controller->GetMatrixHeight();
zones[0].matrix_map.width = controller->GetMatrixWidth();
zones[0].matrix_map.map = controller->GetMatrixMap();
}
for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++)
{
led new_led;
new_led.name = zones[0].name + ", LED " + std::to_string(led_idx + 1);
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_SkydimoSerial::DeviceConfigureZone(int zone_idx)
{
unsigned int zone_index = (unsigned int)zone_idx;
if(zone_index < zones.size())
{
if(zones[zone_index].flags & ZONE_FLAG_MANUALLY_CONFIGURED_SIZE)
{
controller->SetLEDCount(zones[zone_index].leds_count);
}
else
{
controller->SetLEDCount(controller->GetMinLEDCount());
}
zones[zone_index].leds_count = controller->GetLEDCount();
SetupZones();
}
}
void RGBController_SkydimoSerial::DeviceUpdateLEDs()
{
controller->SetLEDs(colors);
}
void RGBController_SkydimoSerial::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_SkydimoSerial::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_SkydimoSerial::DeviceUpdateMode()
{
}
@@ -0,0 +1,33 @@
/*---------------------------------------------------------*\
| RGBController_SkydimoSerial.h |
| |
| RGBController for Skydimo serial devices |
| |
| Bartholomew Ho <qiangqiang101@hotmail.com> 30 Jun 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "SkydimoSerialController.h"
class RGBController_SkydimoSerial : public RGBController
{
public:
RGBController_SkydimoSerial(SkydimoSerialController* controller_ptr);
~RGBController_SkydimoSerial();
void SetupZones();
void DeviceConfigureZone(int zone_idx);
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
private:
SkydimoSerialController* controller;
};
@@ -0,0 +1,548 @@
/*---------------------------------------------------------*\
| SkydimoSerialController.cpp |
| |
| Driver for Skydimo serial devices |
| |
| Bartholomew Ho <qiangqiang101@hotmail.com> 30 Jun 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <algorithm>
#include <cctype>
#include "LogManager.h"
#include "SkydimoSerialController.h"
enum SkydimoLayoutKind
{
SKYDIMO_LAYOUT_NONE,
SKYDIMO_LAYOUT_STRIP_1,
SKYDIMO_LAYOUT_PERIMETER_3,
SKYDIMO_LAYOUT_PERIMETER_4,
SKYDIMO_LAYOUT_SIDES_2
};
struct SkydimoModelEntry
{
const char* model;
SkydimoLayoutKind layout;
unsigned int zone_leds[4];
unsigned int total_leds;
unsigned int max_leds;
};
static const SkydimoModelEntry skydimo_model_entries[] =
{
{ "SK0121", SKYDIMO_LAYOUT_PERIMETER_3, { 13, 25, 13, 0 }, 51, 0 },
{ "SK0124", SKYDIMO_LAYOUT_PERIMETER_3, { 14, 26, 14, 0 }, 54, 0 },
{ "SK0127", SKYDIMO_LAYOUT_PERIMETER_3, { 17, 31, 17, 0 }, 65, 0 },
{ "SK0132", SKYDIMO_LAYOUT_PERIMETER_3, { 20, 37, 20, 0 }, 77, 0 },
{ "SK0134", SKYDIMO_LAYOUT_PERIMETER_3, { 15, 41, 15, 0 }, 71, 0 },
{ "SK0149", SKYDIMO_LAYOUT_PERIMETER_3, { 19, 69, 19, 0 }, 107, 0 },
{ "SK0201", SKYDIMO_LAYOUT_SIDES_2, { 20, 20, 0, 0 }, 40, 0 },
{ "SK0202", SKYDIMO_LAYOUT_SIDES_2, { 30, 30, 0, 0 }, 60, 0 },
{ "SK0204", SKYDIMO_LAYOUT_SIDES_2, { 25, 25, 0, 0 }, 50, 0 },
{ "SK0402", SKYDIMO_LAYOUT_STRIP_1, { 72, 0, 0, 0 }, 72, 0 },
{ "SK0403", SKYDIMO_LAYOUT_STRIP_1, { 96, 0, 0, 0 }, 96, 0 },
{ "SK0404", SKYDIMO_LAYOUT_STRIP_1, { 144, 0, 0, 0 }, 144, 0 },
{ "SK0410", SKYDIMO_LAYOUT_NONE, { 0, 0, 0, 0 }, 0, 300 },
{ "SK0801", SKYDIMO_LAYOUT_STRIP_1, { 2, 0, 0, 0 }, 2, 0 },
{ "SK0802", SKYDIMO_LAYOUT_STRIP_1, { 18, 0, 0, 0 }, 18, 0 },
{ "SK0803", SKYDIMO_LAYOUT_STRIP_1, { 10, 0, 0, 0 }, 10, 0 },
{ "SK0901", SKYDIMO_LAYOUT_STRIP_1, { 14, 0, 0, 0 }, 14, 0 },
{ "SK0E01", SKYDIMO_LAYOUT_STRIP_1, { 16, 0, 0, 0 }, 16, 0 },
{ "SK0F01", SKYDIMO_LAYOUT_SIDES_2, { 29, 29, 0, 0 }, 58, 0 },
{ "SK0F02", SKYDIMO_LAYOUT_SIDES_2, { 25, 25, 0, 0 }, 50, 0 },
{ "SK0H01", SKYDIMO_LAYOUT_STRIP_1, { 2, 0, 0, 0 }, 2, 0 },
{ "SK0H02", SKYDIMO_LAYOUT_STRIP_1, { 4, 0, 0, 0 }, 4, 0 },
{ "SK0K01", SKYDIMO_LAYOUT_STRIP_1, { 120, 0, 0, 0 }, 120, 0 },
{ "SK0K02", SKYDIMO_LAYOUT_STRIP_1, { 15, 0, 0, 0 }, 15, 0 },
{ "SK0L21", SKYDIMO_LAYOUT_PERIMETER_4, { 13, 25, 13, 25 }, 76, 0 },
{ "SK0L24", SKYDIMO_LAYOUT_PERIMETER_4, { 14, 26, 14, 26 }, 80, 0 },
{ "SK0L27", SKYDIMO_LAYOUT_PERIMETER_4, { 17, 31, 17, 31 }, 96, 0 },
{ "SK0L32", SKYDIMO_LAYOUT_PERIMETER_4, { 20, 37, 20, 37 }, 114, 0 },
{ "SK0L34", SKYDIMO_LAYOUT_PERIMETER_4, { 15, 41, 15, 41 }, 112, 0 },
{ "SK0M01", SKYDIMO_LAYOUT_STRIP_1, { 24, 0, 0, 0 }, 24, 0 },
{ "SK0N01", SKYDIMO_LAYOUT_STRIP_1, { 256, 0, 0, 0 }, 256, 0 },
{ "SK0N02", SKYDIMO_LAYOUT_STRIP_1, { 1024, 0, 0, 0 }, 1024, 0 },
{ "SK0N03", SKYDIMO_LAYOUT_STRIP_1, { 253, 0, 0, 0 }, 253, 0 },
{ "SK0S01", SKYDIMO_LAYOUT_STRIP_1, { 32, 0, 0, 0 }, 32, 0 },
{ "SKA124", SKYDIMO_LAYOUT_PERIMETER_3, { 18, 34, 18, 0 }, 70, 0 },
{ "SKA127", SKYDIMO_LAYOUT_PERIMETER_3, { 20, 41, 20, 0 }, 81, 0 },
{ "SKA132", SKYDIMO_LAYOUT_PERIMETER_3, { 25, 45, 25, 0 }, 95, 0 },
{ "SKA134", SKYDIMO_LAYOUT_PERIMETER_3, { 21, 51, 21, 0 }, 93, 0 },
{ "SKB124", SKYDIMO_LAYOUT_PERIMETER_4, { 18, 34, 18, 34 }, 104, 0 },
{ "SKB127", SKYDIMO_LAYOUT_PERIMETER_4, { 20, 41, 20, 41 }, 122, 0 },
{ "SKB132", SKYDIMO_LAYOUT_PERIMETER_4, { 25, 44, 25, 44 }, 138, 0 },
{ "SKB134", SKYDIMO_LAYOUT_PERIMETER_4, { 21, 50, 21, 50 }, 142, 0 }
};
#define SKYDIMO_MODEL_ENTRY_COUNT (sizeof(skydimo_model_entries) / sizeof(skydimo_model_entries[0]))
static const unsigned char skydimo_serial_frame_header[] =
{
0x41, 0x64, 0x61, 0x00
};
static std::string StringToUpper(const std::string& value)
{
std::string result = value;
for(unsigned int char_idx = 0; char_idx < result.size(); char_idx++)
{
result[char_idx] = (char)std::toupper((unsigned char)result[char_idx]);
}
return(result);
}
static const SkydimoModelEntry* FindModelEntry(const std::string& model)
{
for(unsigned int entry_idx = 0; entry_idx < SKYDIMO_MODEL_ENTRY_COUNT; entry_idx++)
{
if(model == skydimo_model_entries[entry_idx].model)
{
return(&skydimo_model_entries[entry_idx]);
}
}
return(nullptr);
}
static std::string BytesToHex(const std::vector<unsigned char>& bytes, unsigned int offset)
{
static const char hex[] = "0123456789ABCDEF";
std::string result;
result.reserve((bytes.size() - offset) * 2);
for(unsigned int byte_idx = offset; byte_idx < bytes.size(); byte_idx++)
{
result.push_back(hex[(bytes[byte_idx] >> 4) & 0x0F]);
result.push_back(hex[bytes[byte_idx] & 0x0F]);
}
return(result);
}
SkydimoSerialController::SkydimoSerialController(const std::string& port_name_arg)
{
port_name = port_name_arg;
model_name = "Skydimo UNKNOWN";
model_id = "UNKNOWN";
serial_id = "000000";
port_open = serial_port_interface.serial_open(port_name.c_str(), SKYDIMO_SERIAL_BAUD_RATE);
present = false;
resizable = true;
leds_count = SKYDIMO_SERIAL_DEFAULT_LED_COUNT;
leds_min = SKYDIMO_SERIAL_DEFAULT_LED_COUNT;
leds_max = SKYDIMO_SERIAL_DEFAULT_MAX_LED_COUNT;
matrix_width = 0;
matrix_height = 0;
if(!port_open)
{
LOG_DEBUG("[SkydimoSerialController] Failed to open serial port %s", port_name.c_str());
}
}
SkydimoSerialController::~SkydimoSerialController()
{
}
bool SkydimoSerialController::IsPresent()
{
if(present)
{
return(true);
}
present = QueryInfo();
return(present);
}
bool SkydimoSerialController::QueryInfo()
{
if(!port_open)
{
return(false);
}
serial_port_interface.serial_flush_rx();
char query[] = SKYDIMO_SERIAL_QUERY;
int bytes_written = serial_port_interface.serial_write(query, SKYDIMO_SERIAL_QUERY_LENGTH);
if(bytes_written != SKYDIMO_SERIAL_QUERY_LENGTH)
{
LOG_DEBUG("[SkydimoSerialController] Failed to send identification query to %s (%d/%u bytes)",
port_name.c_str(), bytes_written, (unsigned int)SKYDIMO_SERIAL_QUERY_LENGTH);
return(false);
}
std::vector<unsigned char> response;
for(unsigned int attempt = 0; attempt < SKYDIMO_SERIAL_QUERY_ATTEMPTS; attempt++)
{
char buffer[SKYDIMO_SERIAL_READ_BUFFER_SIZE];
int bytes_read = serial_port_interface.serial_read(buffer, SKYDIMO_SERIAL_READ_BUFFER_SIZE);
if(bytes_read < 0)
{
LOG_DEBUG("[SkydimoSerialController] Failed to read identification response from %s", port_name.c_str());
return(false);
}
if(bytes_read > 0)
{
response.insert(response.end(), buffer, buffer + bytes_read);
}
if(response.size() >= SKYDIMO_SERIAL_MAX_RESPONSE_SIZE)
{
break;
}
if(!response.empty())
{
unsigned char last = response[response.size() - 1];
unsigned char prev = response.size() >= 2 ? response[response.size() - 2] : 0;
if((last == '\r') || (last == '\n') || (prev == '\r'))
{
break;
}
}
}
while(!response.empty() && ((response.back() == '\r') || (response.back() == '\n')))
{
response.pop_back();
}
if(response.size() < SKYDIMO_SERIAL_MODEL_ID_LENGTH)
{
LOG_TRACE("[SkydimoSerialController] No Skydimo identification response from %s", port_name.c_str());
return(false);
}
if((std::toupper(response[0]) != 'S') || (std::toupper(response[1]) != 'K'))
{
LOG_TRACE("[SkydimoSerialController] Ignoring non-Skydimo serial device at %s", port_name.c_str());
return(false);
}
model_id = StringToUpper(std::string(response.begin(), response.begin() + SKYDIMO_SERIAL_MODEL_ID_LENGTH));
model_name = "Skydimo " + model_id;
serial_id = "000000";
unsigned int comma_offset = (unsigned int)response.size();
for(unsigned int byte_idx = 0; byte_idx < response.size(); byte_idx++)
{
if(response[byte_idx] == ',')
{
comma_offset = byte_idx;
break;
}
}
if(comma_offset < response.size())
{
if(comma_offset < SKYDIMO_SERIAL_MODEL_ID_LENGTH)
{
LOG_DEBUG("[SkydimoSerialController] Invalid identification response from %s", port_name.c_str());
return(false);
}
if((comma_offset + 1) < response.size())
{
serial_id = BytesToHex(response, comma_offset + 1);
}
}
ConfigureOutput();
return(true);
}
void SkydimoSerialController::ConfigureOutput()
{
const SkydimoModelEntry* entry = FindModelEntry(model_id);
resizable = true;
leds_count = SKYDIMO_SERIAL_DEFAULT_LED_COUNT;
leds_min = SKYDIMO_SERIAL_DEFAULT_LED_COUNT;
leds_max = SKYDIMO_SERIAL_DEFAULT_MAX_LED_COUNT;
matrix_width = 0;
matrix_height = 0;
matrix_map.clear();
if((entry != nullptr) && (entry->max_leds != 0))
{
leds_max = entry->max_leds;
}
if((entry == nullptr) || (entry->layout == SKYDIMO_LAYOUT_NONE))
{
return;
}
resizable = false;
leds_count = entry->total_leds;
leds_min = entry->total_leds;
leds_max = entry->total_leds;
if(entry->layout == SKYDIMO_LAYOUT_STRIP_1)
{
return;
}
const unsigned int zone_1_leds = entry->zone_leds[0];
const unsigned int zone_2_leds = entry->zone_leds[1];
const unsigned int zone_3_leds = entry->zone_leds[2];
const unsigned int zone_4_leds = entry->zone_leds[3];
if(entry->layout == SKYDIMO_LAYOUT_PERIMETER_4)
{
matrix_height = std::max(zone_1_leds, zone_3_leds) + 2;
matrix_width = std::max(zone_2_leds, zone_4_leds) + 2;
}
else if(entry->layout == SKYDIMO_LAYOUT_PERIMETER_3)
{
matrix_height = std::max(zone_1_leds, zone_3_leds) + 1;
matrix_width = zone_2_leds + 2;
}
else
{
matrix_height = std::max(zone_1_leds, zone_2_leds) + 2;
matrix_width = std::max(3U, ((16 * matrix_height) + 4) / 9);
}
matrix_map.assign(matrix_width * matrix_height, SKYDIMO_SERIAL_MATRIX_NA);
unsigned int led_index = 0;
if(entry->layout == SKYDIMO_LAYOUT_SIDES_2)
{
unsigned int placed = 0;
int y = (int)matrix_height - 2;
while((placed < zone_1_leds) && (y >= 1))
{
SetMatrixCell((unsigned int)y, 0, led_index);
placed++;
y--;
}
}
else
{
unsigned int placed = 0;
int y = entry->layout == SKYDIMO_LAYOUT_PERIMETER_3 ? (int)matrix_height - 1
: (int)matrix_height - 2;
while((placed < zone_1_leds) && (y >= 1))
{
SetMatrixCell((unsigned int)y, matrix_width - 1, led_index);
placed++;
y--;
}
}
if((entry->layout == SKYDIMO_LAYOUT_PERIMETER_3) || (entry->layout == SKYDIMO_LAYOUT_PERIMETER_4))
{
unsigned int placed = 0;
int x = (int)matrix_width - 2;
while((placed < zone_2_leds) && (x >= 1))
{
SetMatrixCell(0, (unsigned int)x, led_index);
placed++;
x--;
}
}
else if(entry->layout == SKYDIMO_LAYOUT_SIDES_2)
{
unsigned int placed = 0;
unsigned int y = 1;
while((placed < zone_2_leds) && (y <= (matrix_height - 2)))
{
SetMatrixCell(y, matrix_width - 1, led_index);
placed++;
y++;
}
}
unsigned int end_y = entry->layout == SKYDIMO_LAYOUT_PERIMETER_3 ? matrix_height - 1 : matrix_height - 2;
unsigned int placed = 0;
unsigned int y = 1;
while((placed < zone_3_leds) && (y <= end_y))
{
SetMatrixCell(y, 0, led_index);
placed++;
y++;
}
if(entry->layout == SKYDIMO_LAYOUT_PERIMETER_4)
{
placed = 0;
unsigned int x = 1;
while((placed < zone_4_leds) && (x <= (matrix_width - 2)))
{
SetMatrixCell(matrix_height - 1, x, led_index);
placed++;
x++;
}
}
if(led_index != entry->total_leds)
{
LOG_ERROR("[SkydimoSerialController] Invalid matrix map for %s (%u/%u LEDs placed)",
model_id.c_str(), led_index, entry->total_leds);
}
}
void SkydimoSerialController::SetMatrixCell(unsigned int y, unsigned int x, unsigned int& led_index)
{
if((y < matrix_height) && (x < matrix_width))
{
matrix_map[(y * matrix_width) + x] = led_index++;
}
}
bool SkydimoSerialController::WriteFrame(const std::vector<RGBColor>& colors, unsigned int count)
{
std::lock_guard<std::mutex> guard(device_mutex);
if(!port_open)
{
return(false);
}
if(count > colors.size())
{
count = (unsigned int)colors.size();
}
std::vector<unsigned char> packet;
packet.reserve(sizeof(skydimo_serial_frame_header) + SKYDIMO_SERIAL_FRAME_COUNT_SIZE + (count * SKYDIMO_SERIAL_BYTES_PER_LED));
for(unsigned int header_idx = 0; header_idx < sizeof(skydimo_serial_frame_header); header_idx++)
{
packet.push_back(skydimo_serial_frame_header[header_idx]);
}
packet.push_back((unsigned char)((count >> 8) & 0xFF));
packet.push_back((unsigned char)(count & 0xFF));
for(unsigned int led_idx = 0; led_idx < count; led_idx++)
{
packet.push_back(RGBGetRValue(colors[led_idx]));
packet.push_back(RGBGetGValue(colors[led_idx]));
packet.push_back(RGBGetBValue(colors[led_idx]));
}
int packet_size = (int)packet.size();
int total_bytes_written = 0;
while(total_bytes_written < packet_size)
{
int bytes_written = serial_port_interface.serial_write((char*)packet.data() + total_bytes_written,
packet_size - total_bytes_written);
if(bytes_written <= 0)
{
LOG_ERROR("[SkydimoSerialController] Failed to write RGB frame to %s (%d/%d bytes)",
port_name.c_str(), total_bytes_written, packet_size);
return(false);
}
total_bytes_written += bytes_written;
}
return(true);
}
void SkydimoSerialController::SetLEDs(const std::vector<RGBColor>& colors)
{
WriteFrame(colors, leds_count);
}
void SkydimoSerialController::SetBlack()
{
std::vector<RGBColor> black_colors;
black_colors.resize(leds_count, ToRGBColor(0, 0, 0));
WriteFrame(black_colors, leds_count);
}
void SkydimoSerialController::SetLEDCount(unsigned int led_count)
{
if(!resizable)
{
return;
}
if((led_count < leds_min) || (led_count > leds_max))
{
LOG_WARNING("[SkydimoSerialController] Ignoring invalid LED count %u for %s (valid range %u-%u)",
led_count, model_id.c_str(), leds_min, leds_max);
return;
}
leds_count = led_count;
}
std::string SkydimoSerialController::GetName() const
{
return(model_name);
}
std::string SkydimoSerialController::GetSerial() const
{
return(serial_id);
}
std::string SkydimoSerialController::GetLocation() const
{
return("Serial: " + port_name);
}
bool SkydimoSerialController::IsResizable() const
{
return(resizable);
}
unsigned int SkydimoSerialController::GetLEDCount() const
{
return(leds_count);
}
unsigned int SkydimoSerialController::GetMinLEDCount() const
{
return(leds_min);
}
unsigned int SkydimoSerialController::GetMaxLEDCount() const
{
return(leds_max);
}
unsigned int SkydimoSerialController::GetMatrixWidth() const
{
return(matrix_width);
}
unsigned int SkydimoSerialController::GetMatrixHeight() const
{
return(matrix_height);
}
const std::vector<unsigned int>& SkydimoSerialController::GetMatrixMap() const
{
return(matrix_map);
}
@@ -0,0 +1,82 @@
/*---------------------------------------------------------*\
| SkydimoSerialController.h |
| |
| Driver for Skydimo serial devices |
| |
| Bartholomew Ho <qiangqiang101@hotmail.com> 30 Jun 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <mutex>
#include <string>
#include <vector>
#include "RGBController.h"
#include "serial_port.h"
#define SKYDIMO_SERIAL_BAUD_RATE 115200
#define SKYDIMO_SERIAL_QUERY "Moni-A"
#define SKYDIMO_SERIAL_QUERY_LENGTH 6
#define SKYDIMO_SERIAL_MODEL_ID_LENGTH 6
#define SKYDIMO_SERIAL_READ_BUFFER_SIZE 64
#define SKYDIMO_SERIAL_MAX_RESPONSE_SIZE 63
#define SKYDIMO_SERIAL_QUERY_ATTEMPTS 10
#define SKYDIMO_SERIAL_DEFAULT_LED_COUNT 0
#define SKYDIMO_SERIAL_DEFAULT_MAX_LED_COUNT 150
#define SKYDIMO_SERIAL_BYTES_PER_LED 3
#define SKYDIMO_SERIAL_FRAME_COUNT_SIZE 2
#define SKYDIMO_SERIAL_MATRIX_NA 0xFFFFFFFF
class SkydimoSerialController
{
public:
SkydimoSerialController(const std::string& port_name_arg);
~SkydimoSerialController();
bool IsPresent();
void SetLEDs(const std::vector<RGBColor>& colors);
void SetBlack();
void SetLEDCount(unsigned int led_count);
std::string GetName() const;
std::string GetSerial() const;
std::string GetLocation() const;
bool IsResizable() const;
unsigned int GetLEDCount() const;
unsigned int GetMinLEDCount() const;
unsigned int GetMaxLEDCount() const;
unsigned int GetMatrixWidth() const;
unsigned int GetMatrixHeight() const;
const std::vector<unsigned int>& GetMatrixMap() const;
private:
bool QueryInfo();
void ConfigureOutput();
void SetMatrixCell(unsigned int y, unsigned int x, unsigned int& led_index);
bool WriteFrame(const std::vector<RGBColor>& colors, unsigned int count);
std::string port_name;
std::string model_name;
std::string model_id;
std::string serial_id;
bool port_open;
bool present;
bool resizable;
unsigned int leds_count;
unsigned int leds_min;
unsigned int leds_max;
unsigned int matrix_width;
unsigned int matrix_height;
std::vector<unsigned int> matrix_map;
serial_port serial_port_interface;
std::mutex device_mutex;
};
@@ -0,0 +1,55 @@
/*---------------------------------------------------------*\
| SkydimoSerialControllerDetect.cpp |
| |
| Detector for Skydimo serial devices |
| |
| Bartholomew Ho <qiangqiang101@hotmail.com> 30 Jun 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <vector>
#include "DetectionManager.h"
#include "LogManager.h"
#include "RGBController_SkydimoSerial.h"
#include "SkydimoSerialController.h"
#include "find_usb_serial_port.h"
/*---------------------------------------------------------*\
| Skydimo serial vendor and product IDs |
\*---------------------------------------------------------*/
#define SKYDIMO_SERIAL_VID 0x1A86
#define SKYDIMO_SERIAL_PID 0x7523
DetectedControllers DetectSkydimoSerialControllers()
{
DetectedControllers detected_controllers;
std::vector<std::string> ports = find_usb_serial_port(SKYDIMO_SERIAL_VID, SKYDIMO_SERIAL_PID);
for(unsigned int port_idx = 0; port_idx < ports.size(); port_idx++)
{
SkydimoSerialController* controller = new SkydimoSerialController(ports[port_idx]);
if(controller->IsPresent())
{
RGBController_SkydimoSerial* rgb_controller = new RGBController_SkydimoSerial(controller);
detected_controllers.push_back(rgb_controller);
LOG_INFO("[SkydimoSerialControllerDetect] Detected %s at %s",
controller->GetName().c_str(), ports[port_idx].c_str());
}
else
{
delete controller;
}
}
return(detected_controllers);
}
REGISTER_DETECTOR("Skydimo Serial", DetectSkydimoSerialControllers);
/*---------------------------------------------------------------------------------------------------------*\
| Entries for dynamic UDEV rules |
| |
| DUMMY_DEVICE_DETECTOR("Skydimo Serial", DetectSkydimoSerialControllers, 0x1A86, 0x7523 ) |
\*---------------------------------------------------------------------------------------------------------*/
@@ -15,7 +15,7 @@
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#include "SteelSeriesMouseController.h"
#define STEELSERIES_AEORX3_NAME "SteelSeries Aerox 3"
@@ -15,7 +15,7 @@
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#include "SteelSeriesMouseController.h"
#define STEELSERIES_AEROX5_NAME "SteelSeries Aerox 5"
@@ -11,7 +11,6 @@
#include "SteelSeriesAeroxWirelessController.h"
#include "LogManager.h"
#include "SteelSeriesGeneric.h"
SteelSeriesAeroxWirelessController::SteelSeriesAeroxWirelessController(hid_device* dev_handle, steelseries_type proto_type, const char* path, std::string dev_name) : SteelSeriesMouseController(dev_handle, proto_type, path, dev_name)
{
@@ -15,7 +15,7 @@
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#include "SteelSeriesMouseController.h"
#define STEELSERIES_AEROX3_WIRELESS_NAME "SteelSeries Aerox 3 Wireless (2.4 GHz wireless mode)"
@@ -14,7 +14,7 @@
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#define STEELSERIES_APEX3_BRIGHTNESS_MIN 0x00
#define STEELSERIES_APEX3_HID_TIMEOUT 100
@@ -15,7 +15,7 @@
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#include "SteelSeriesApex3Controller.h"
#define STEELSERIES_8Z_LED_COUNT 8
@@ -1,160 +0,0 @@
/*---------------------------------------------------------*\
| SteelSeriesApex9Controller.cpp |
| |
| Driver for SteelSeries Apex 9 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
#include <cstring>
#include "SteelSeriesApex9Controller.h"
using namespace std::chrono_literals;
static unsigned int keys[] = {0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, //20
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, //40
0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, //60
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x64, 0xE0, //80
0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xF0, 0x31, 0x87,
0x88, 0x89, 0x8A, 0x8B, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, //100
0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62,
0x63 };
SteelSeriesApex9Controller::SteelSeriesApex9Controller(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name) : SteelSeriesApexBaseController (dev_handle, path, dev_name)
{
proto_type = type;
}
SteelSeriesApex9Controller::~SteelSeriesApex9Controller()
{
hid_close(dev);
}
void SteelSeriesApex9Controller::SetMode(unsigned char mode /*mode*/, std::vector<RGBColor> /*colors*/ )
{
unsigned char mode_colors[9];
active_mode = mode;
memset(mode_colors, 0x00, sizeof(mode_colors));
}
void SteelSeriesApex9Controller::SetLEDsDirect(std::vector<RGBColor> colors)
{
unsigned char buf[APEX_9_PACKET_LENGTH];
int num_keys = 0;
num_keys = sizeof(keys) / sizeof(*keys);
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
/*-----------------------------------------------------*\
| Set up Direct packet |
\*-----------------------------------------------------*/
buf[0x00] = 0;
buf[0x01] = APEX_9_PACKET_ID_DIRECT;
buf[0x02] = num_keys;
/*-----------------------------------------------------*\
| Fill in color data |
\*-----------------------------------------------------*/
for(int i = 0; i < num_keys; i++)
{
buf[(i*4)+3] = keys[i];
buf[(i*4)+4] = RGBGetRValue(colors[i]);
buf[(i*4)+5] = RGBGetGValue(colors[i]);
buf[(i*4)+6] = RGBGetBValue(colors[i]);
}
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_send_feature_report(dev, buf, APEX_9_PACKET_LENGTH);
}
std::string SteelSeriesApex9Controller::GetSerial()
{
std::string return_string = "";
switch(proto_type)
{
case APEX_9_TKL:
return_string = "64847";
break;
case APEX_9_MINI:
return_string = "64837";
break;
default:
return_string = "Apex 9 GetSerial() error";
}
return(return_string);
}
std::string SteelSeriesApex9Controller::GetVersion()
{
std::string return_string = "Unsupported protocol";
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE];
int result;
memset(obuf, 0x00, sizeof(obuf));
obuf[0x00] = 0;
obuf[0x01] = 0x90;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2);
if(result > 0)
{
std::string fwver(ibuf, ibuf+STEELSERIES_PACKET_IN_SIZE);
fwver = fwver.substr(2, fwver.size());
fwver = fwver.c_str();
/*---------------------------------------------*\
| Find 2 periods in string, if found we can |
| form a X.Y.Z revision. |
\*---------------------------------------------*/
std::size_t majorp = fwver.find('.');
if(majorp != std::string::npos)
{
std::size_t minorp = fwver.find('.', majorp+1);
if(minorp != std::string::npos)
{
std::string major = fwver.substr(0, majorp);
std::string minor = fwver.substr(majorp+1, (minorp-majorp-1));
std::string build = fwver.substr(minorp+1);
return_string = "KBD: " + major + "." + minor + "." + build;
}
}
}
return(return_string);
}
/*-------------------------------------------------------------------------------------------------*\
| Private packet sending functions. |
\*-------------------------------------------------------------------------------------------------*/
void SteelSeriesApex9Controller::SelectProfile(unsigned char profile)
{
unsigned char buf[65];
/*-----------------------------------------------------*\
| Zero out buffer, set up packet and send |
\*-----------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0;
buf[0x01] = 0x89;
buf[0x02] = profile;
hid_send_feature_report(dev, buf, 65);
}
@@ -1,38 +0,0 @@
/*---------------------------------------------------------*\
| SteelSeriesApex9Controller.h |
| |
| Driver for SteelSeries Apex 9 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesApexBaseController.h"
enum
{
APEX_9_PACKET_ID_DIRECT = 0x40, /* Direct mode */
APEX_9_PACKET_LENGTH = 513,
};
class SteelSeriesApex9Controller : public SteelSeriesApexBaseController
{
public:
SteelSeriesApex9Controller(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name);
~SteelSeriesApex9Controller();
void SetMode(unsigned char mode, std::vector<RGBColor> colors);
void SetLEDsDirect(std::vector<RGBColor> colors);
std::string GetSerial() override;
std::string GetVersion() override;
private:
void SelectProfile(unsigned char profile);
};
@@ -37,7 +37,7 @@ std::string SteelSeriesApexBaseController::GetName()
/*---------------------------------------------------------*\
| Gen 1 Apex Pro stores the unit serial number in firmware. |
| The first 5 digits determine the region of the keyboard. |
| This is not the case for Gen 3, call to this function |
| This is not the case for Gen 2+, calls to this function |
| will be ignored. |
\*---------------------------------------------------------*/
std::string SteelSeriesApexBaseController::GetSerial()
@@ -51,7 +51,7 @@ std::string SteelSeriesApexBaseController::GetSerial()
memset(obuf, 0x00, sizeof(obuf));
obuf[0x00] = 0;
obuf[0x01] = 0xFF;
obuf[0x01] = APEX_GEN1_PACKET_ID_SERIAL;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2);
@@ -97,8 +97,8 @@ std::string SteelSeriesApexBaseController::GetVersion()
if(proto_type == APEX)
{
/*-------------------------------------------------*\
| Gen 1 & 2 Apex Pro report KBD and LED firmware |
| Gen 3 only reports the KBD firmware, ignoring |
| Gen 1 Apex Pro reports KBD and LED firmware |
| Gen 2+ only reports the KBD firmware, ignoring |
| requests to read the LED version |
\*-------------------------------------------------*/
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
@@ -107,7 +107,7 @@ std::string SteelSeriesApexBaseController::GetVersion()
memset(obuf, 0x00, sizeof(obuf));
obuf[0x00] = 0;
obuf[0x01] = 0x90;
obuf[0x01] = APEX_PACKET_ID_FIRMWARE;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2);
@@ -117,9 +117,9 @@ std::string SteelSeriesApexBaseController::GetVersion()
fwver.erase(std::remove(fwver.begin(), fwver.end(), '\0'), fwver.end());
/*---------------------------------------------*\
| Apex Pro Gen 3 needs the first char dropped |
| Apex Pro Gen 2+ needs the first char dropped |
\*---------------------------------------------*/
if(kbd_quirk == APEX_GEN3)
if(ibuf[0] == APEX_PACKET_ID_FIRMWARE)
{
fwver.erase(0,1);
}
@@ -128,9 +128,9 @@ std::string SteelSeriesApexBaseController::GetVersion()
}
/*-------------------------------------------------*\
| Clear and reuse buffer |
| Get LED firmware for Gen 1 |
\*-------------------------------------------------*/
if(kbd_quirk != APEX_GEN3)
if(kbd_quirk == APEX_GEN1)
{
memset(ibuf, 0x00, sizeof(ibuf));
obuf[0x02] = 0x01;
@@ -13,18 +13,23 @@
#include <cstring>
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#define STEELSERIES_PACKET_IN_SIZE 64
#define STEELSERIES_PACKET_OUT_SIZE STEELSERIES_PACKET_IN_SIZE + 1
#define STEELSERIES_PACKET_IN_SIZE 64
#define STEELSERIES_PACKET_OUT_SIZE STEELSERIES_PACKET_IN_SIZE + 1
/*-------------------------------------------------*\
| Gen 1: 2019-22 models (all FW) & 2023 FW < 1.19.7 |
| Gen 2: 2023 models with FW >= 1.19.7 |
| Gen 3: 2025+ and may feature Gen 3 in the name |
| Protocol quirk applicability |
| |
| Gen 1: 2019-22 models with original case design, |
| all FW releases. |
| Gen 2: '2023' USB-C + Apex 9 series TKL and |
| smaller, wired/wireless with pre-prism FW |
| Gen 3: All models with Omnipoint 3.0 switches and |
| Gen 2 models with prism FW update |
\*-------------------------------------------------*/
typedef enum
{
APEX_GEN1 = 0x00,
@@ -33,6 +38,28 @@ typedef enum
} protocol_quirk;
enum
{
APEX_MODE_DIRECT,
APEX_MODE_ONBOARD,
};
enum
{
APEX_PACKET_LENGTH = 643,
APEX_9_PACKET_LENGTH = 513, /* Apex 9 direct report size */
APEX_PACKET_ID_PROFILE = 0x89, /* Profile */
APEX_PACKET_ID_FIRMWARE = 0x90, /* Firmware version */
APEX_GEN1_PACKET_ID_DIRECT = 0x3A, /* Direct mode */
APEX_GEN1_PACKET_ID_ONBOARD = 0x3B, /* Onboard mode */
APEX_GEN1_PACKET_ID_SERIAL = 0xFF, /* Serial number */
APEX_2023_PACKET_ID_DIRECT = 0x40, /* New Wired Direct mode */
APEX_2023_PACKET_ID_DIRECT_WIRELESS = 0x61, /* New Wireless Direct mode */
APEX_2023_PACKET_ID_INIT = 0x4B, /* New Initialization */
APEX_2023_PACKET_ID_ONBOARD = 0x41, /* New Wired onboard mode */
APEX_2023_PACKET_ID_REGION = 0xF5, /* Region byte */
};
class SteelSeriesApexBaseController
{
public:
@@ -56,4 +83,6 @@ protected:
std::string location;
std::string name;
protocol_quirk kbd_quirk;
unsigned short device_pid;
unsigned char reset_cmd;
};
@@ -55,11 +55,18 @@ RGBController_SteelSeriesApex::RGBController_SteelSeriesApex(SteelSeriesApexBase
mode Direct;
Direct.name = "Direct";
Direct.value = 0x00;
Direct.value = APEX_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
mode Onboard;
Onboard.name = "Onboard";
Onboard.value = APEX_MODE_ONBOARD;
Onboard.flags = 0;
Onboard.color_mode = MODE_COLORS_NONE;
modes.push_back(Onboard);
SetupZones();
}
@@ -97,13 +104,13 @@ void RGBController_SteelSeriesApex::SetupZones()
new_zone.matrix_map.width = MATRIX_WIDTH;
new_zone.matrix_map.map.resize(MATRIX_HEIGHT * MATRIX_WIDTH);
if((proto_type == APEX) || (proto_type == APEX_M) || (proto_type == APEX_9_TKL) || (proto_type == APEX_9_MINI))
if((proto_type == APEX) || (proto_type == APEX_M))
{
SetSkuRegion(&new_zone.matrix_map, sku);
}
}
if((proto_type == APEX) || (proto_type == APEX_M) || (proto_type == APEX_9_TKL) || (proto_type == APEX_9_MINI))
if((proto_type == APEX) || (proto_type == APEX_M))
{
new_zone.leds_min = zone_sizes[zone_idx];
new_zone.leds_max = zone_sizes[zone_idx];
@@ -14,7 +14,7 @@
#include <chrono>
#include "RGBController.h"
#include "SteelSeriesApexBaseController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
class RGBController_SteelSeriesApex : public RGBController
{
@@ -20,81 +20,133 @@
using namespace std::chrono_literals;
#define FIRMWARE_REQ_LEN 645
static unsigned int keys[] =
{
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, //20
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, //40
0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, //60
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x64, 0xE0, //80
0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xF0, 0x31, 0x87,
0x88, 0x89, 0x8A, 0x8B, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, //100
0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62,
0x63, 0xFB
};
static unsigned int keys[] = {0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, //20
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, //40
0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, //60
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x64, 0xE0, //80
0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xF0, 0x31, 0x87,
0x88, 0x89, 0x8A, 0x8B, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, //100
0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62,
0x63, 0xFB };
/*---------------------------------------------------------*\
| Models whose direct lighting feature report is shorter |
| than APEX_PACKET_LENGTH. Sending the wrong report size |
| makes the keyboard stall the control transfer, so any |
| model listed here would silently ignore lighting updates |
| if the default length were used. |
\*---------------------------------------------------------*/
inline static const std::map<unsigned short, unsigned int> direct_packet_length_map =
{
{STEELSERIES_APEX_9_TKL_PID, APEX_9_PACKET_LENGTH},
{STEELSERIES_APEX_9_MINI_PID, APEX_9_PACKET_LENGTH},
};
SteelSeriesApexController::SteelSeriesApexController(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name) : SteelSeriesApexBaseController(dev_handle, path, dev_name)
inline static const std::map<unsigned short, protocol_quirk> protocol_map =
{
{STEELSERIES_APEX_9_TKL_PID, APEX_GEN2},
{STEELSERIES_APEX_9_MINI_PID, APEX_GEN2},
{STEELSERIES_APEX_PRO_TKL_2023_PID, APEX_GEN2},
{STEELSERIES_APEX_PRO_TKL_2023_WL_PID_1, APEX_GEN3},
{STEELSERIES_APEX_PRO_TKL_2023_WL_PID_2, APEX_GEN3},
{STEELSERIES_APEX_PRO_TKL_GEN3_PID, APEX_GEN3},
{STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1, APEX_GEN3},
{STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2, APEX_GEN3},
};
SteelSeriesApexController::SteelSeriesApexController(hid_device* dev_handle, steelseries_type type, const char* path, unsigned short pid, std::string dev_name) : SteelSeriesApexBaseController(dev_handle, path, dev_name)
{
proto_type = type;
kbd_quirk = APEX_GEN1;
device_pid = pid;
direct_packet_length = APEX_PACKET_LENGTH;
if(protocol_map.count(device_pid) > 0)
{
kbd_quirk = protocol_map.at(device_pid);
}
if(direct_packet_length_map.count(device_pid) > 0)
{
direct_packet_length = direct_packet_length_map.at(device_pid);
}
SendInitialization();
}
SteelSeriesApexController::~SteelSeriesApexController()
{
/*-----------------------------------------------------*\
| Gen 3 models must be explicitly cleared for on-board |
| config selection to apply without power cycling after |
| OpenRGB shuts down. |
\*-----------------------------------------------------*/
if(kbd_quirk == APEX_GEN3)
{
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
memset(obuf, 0x00, sizeof(obuf));
obuf[0x00] = 0;
obuf[0x01] = APEX_GEN3_PACKET_CLEAR_LIGHTING;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
}
SendDeinitialization();
hid_close(dev);
}
void SteelSeriesApexController::SetMode
(
unsigned char mode,
std::vector<RGBColor> /*colors*/
)
/*---------------------------------------------------*\
| OpenRGB must clear initialized keyboards during |
| shutdown as hardware lighting profiles won't apply |
| otherwise. Keyboards not needing initialization are |
| also cleared, though selecting a hardware profile |
| will override the leds unlike the former. |
\*---------------------------------------------------*/
void SteelSeriesApexController::SendDeinitialization()
{
unsigned char mode_colors[9];
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
memset(obuf, 0x00, sizeof(obuf));
obuf[0x00] = 0;
obuf[0x01] = reset_cmd;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
}
void SteelSeriesApexController::SetMode(unsigned char mode, std::vector<RGBColor>)
{
if(active_mode != mode)
{
if(active_mode == APEX_MODE_ONBOARD && kbd_quirk != APEX_GEN1)
{
SendInitialization();
}
if(mode == APEX_MODE_ONBOARD)
{
SendDeinitialization();
}
}
active_mode = mode;
memset(mode_colors, 0x00, sizeof(mode_colors));
}
void SteelSeriesApexController::SetLEDsDirect(std::vector<RGBColor> colors)
{
unsigned char buf[643];
int num_keys = 0;
unsigned char buf[APEX_PACKET_LENGTH];
int num_keys = sizeof(keys) / sizeof(*keys);
unsigned char packet_id = APEX_GEN1_PACKET_ID_DIRECT;
unsigned char packet_id = APEX_PACKET_ID_DIRECT;
/*-----------------------------------------------------*\
| Never write past the end of the report or past |
| the end of the color vector |
\*-----------------------------------------------------*/
if((unsigned int)num_keys > colors.size())
{
num_keys = (int)colors.size();
}
num_keys = sizeof(keys) / sizeof(*keys);
if((unsigned int)((num_keys * 4) + 3) > direct_packet_length)
{
num_keys = (int)((direct_packet_length - 3) / 4);
}
if(kbd_quirk >= APEX_GEN2)
{
struct hid_device_info* info = hid_get_device_info(dev);
/*-------------------------------------------------*\
| Apparently Gen 3 wireless models reuse this |
| protocol, make sure to place their PID here and |
| protocol, make sure to place their model here and |
| further below when developing. |
\*-------------------------------------------------*/
if(info && (info->product_id == 0x1630 || info->product_id == 0x1632
|| info->product_id == 0x162C || info->product_id == 0x162D
|| info->product_id == 0x1644 || info->product_id == 0x1646))
if(device_pid == STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1 ||
device_pid == STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2 ||
device_pid == STEELSERIES_APEX_PRO_TKL_2023_WL_PID_1 ||
device_pid == STEELSERIES_APEX_PRO_TKL_2023_WL_PID_2)
{
packet_id = APEX_2023_PACKET_ID_DIRECT_WIRELESS;
}
@@ -114,7 +166,7 @@ void SteelSeriesApexController::SetLEDsDirect(std::vector<RGBColor> colors)
\*-----------------------------------------------------*/
buf[0x00] = 0;
buf[0x01] = packet_id;
buf[0x02] = kbd_quirk ? (unsigned char)colors.size() : num_keys;
buf[0x02] = (unsigned char)num_keys;
/*-----------------------------------------------------*\
| Fill in color data |
@@ -130,189 +182,156 @@ void SteelSeriesApexController::SetLEDsDirect(std::vector<RGBColor> colors)
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_send_feature_report(dev, buf, 643);
int result = hid_send_feature_report(dev, buf, direct_packet_length);
if(result < 0)
{
LOG_DEBUG("[%s] Direct lighting update failed, %u byte report", name.c_str(), direct_packet_length);
}
}
/*---------------------------------------------------------*\
| Private packet sending functions. |
\*---------------------------------------------------------*/
void SteelSeriesApexController::SelectProfile
(
unsigned char profile
)
void SteelSeriesApexController::SelectProfile(unsigned char profile)
{
unsigned char buf[65];
unsigned char buf[STEELSERIES_PACKET_OUT_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer, set up packet and send |
\*-----------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0;
buf[0x01] = 0x89;
buf[0x01] = APEX_PACKET_ID_PROFILE;
buf[0x02] = profile;
hid_send_feature_report(dev, buf, 65);
hid_send_feature_report(dev, buf, STEELSERIES_PACKET_OUT_SIZE);
}
void SteelSeriesApexController::SendInitialization()
{
unsigned char buf[FIRMWARE_REQ_LEN];
unsigned char read_buf[65];
int res = 0;
char version_str[65] = "Unknown";
struct hid_device_info* info = hid_get_device_info(dev);
unsigned short pid = (info) ? info->product_id : 0;
/*-----------------------------------------------------*\
| Firmware check for TKL 2023 |
\*-----------------------------------------------------*/
if(pid == 0x1628)
/*---------------------------------------------------*\
| Check models which can be Gen 3 via firmware update |
\*---------------------------------------------------*/
if(device_pid == STEELSERIES_APEX_PRO_TKL_PID ||
device_pid == STEELSERIES_APEX_9_TKL_PID ||
device_pid == STEELSERIES_APEX_9_MINI_PID)
{
/*-------------------------------------------------*\
| Zero out buffer |
\*-------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x00;
buf[0x01] = 0x90;
/*-------------------------------------------------*\
| Send packet |
\*-------------------------------------------------*/
hid_write(dev, buf, 65);
/*-------------------------------------------------*\
| Read Response |
\*-------------------------------------------------*/
memset(read_buf, 0x00, sizeof(read_buf));
res = hid_read_timeout(dev, read_buf, sizeof(read_buf), 200);
/*-------------------------------------------------*\
| Firmware Check |
\*-------------------------------------------------*/
if(res > 2 && read_buf[0] == 0x90)
std::string fwver = GetVersion();
fwver.erase(0,4);
int major = 0, minor = 0, patch = 0;
int count = sscanf(fwver.c_str(), "%d.%d.%d", &major, &minor, &patch);
if(count == 3)
{
int major = 0, minor = 0, patch = 0;
char* fw_ptr = (char*)&read_buf[2];
snprintf(version_str, sizeof(version_str), "%s", fw_ptr);
int count = sscanf(version_str, "%d.%d.%d", &major, &minor, &patch);
if(count == 3)
/*--------------------------------------------*\
| Assume Gen 3 protocol from 1.19.7 or newer. |
\*--------------------------------------------*/
if(major > 1)
{
/*-----------------------------------------*\
| Currently set to 1.19.7 or newer. |
\*-----------------------------------------*/
if(major > 1)
kbd_quirk = APEX_GEN3;
}
else if(major == 1)
{
if(minor > 19)
{
kbd_quirk = APEX_GEN2;
kbd_quirk = APEX_GEN3;
}
else if(major == 1)
else if(minor == 19 && patch >= 7)
{
if(minor > 19)
{
kbd_quirk = APEX_GEN2;
}
else if(minor == 19 && patch >= 7)
{
kbd_quirk = APEX_GEN2;
}
kbd_quirk = APEX_GEN3;
}
}
}
}
/*-----------------------------------------------------*\
| Apparently Gen 3 models reuse this protocol, make |
| sure to place their PID here and further above for |
| wireless when developing. |
\*-----------------------------------------------------*/
else if(pid == 0x1630 || pid == 0x1632
|| pid == 0x162C || pid == 0x162D
|| pid == 0x1642 || pid == 0x1644 || pid == 0x1646)
{
kbd_quirk = APEX_GEN3;
}
/*-----------------------------------------------------*\
| Send Initialization packet on new protocol. |
| Send Initialization packet for Gen 3 protocol. This |
| seems to be required for some models and not others, |
| regardless there is no harm in sending it. |
\*-----------------------------------------------------*/
if(kbd_quirk >= APEX_GEN2)
if(kbd_quirk >= APEX_GEN3)
{
unsigned char buf[STEELSERIES_PACKET_OUT_SIZE];
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x00;
buf[0x01] = APEX_2023_PACKET_ID_INIT;
hid_send_feature_report(dev, buf, APEX_2023_PACKET_LENGTH);
LOG_DEBUG("[%s] Using Apex 2023 protocol. FW: %s", name.c_str(), version_str);
reset_cmd = APEX_2023_PACKET_ID_ONBOARD;
buf[0x00] = 0x00;
buf[0x01] = APEX_2023_PACKET_ID_INIT;
hid_send_feature_report(dev, buf, STEELSERIES_PACKET_OUT_SIZE);
LOG_DEBUG("[%s] Using Apex 2023/Gen3 protocol.", name.c_str());
}
else
{
LOG_DEBUG("[%s] Using Apex Legacy protocol. FW: %s", name.c_str(), version_str);
reset_cmd = APEX_GEN1_PACKET_ID_ONBOARD;
LOG_DEBUG("[%s] Using Apex Legacy protocol.", name.c_str());
}
}
std::string SteelSeriesApexController::GetSerial()
{
if(kbd_quirk == APEX_GEN1)
{
return SteelSeriesApexBaseController::GetSerial();
}
/*-------------------------------------------------*\
| Gen 3 doesn't expose the serial number in |
| Gen 2+ doesn't expose the serial number in |
| firmware. A region code is instead set by the |
| user and subsequently read back. This region code |
| is used by all 5 on-board configs. |
| For consistency with other Apex keyboards, this |
| region code in combination with the PID is mapped |
| region code in tandem with the model is mapped |
| to an approximate product number as the region |
| patch logic from that point is identical. |
| The product number used may not be an exact match |
| for the keyboard but should reflect the form |
| factor, region and RGB layout |
\*-------------------------------------------------*/
if(kbd_quirk >= APEX_GEN2)
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE];
int result;
memset(obuf, 0x00, sizeof(obuf));
if(device_pid == STEELSERIES_APEX_PRO_TKL_GEN3_PID)
{
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE];
int result;
struct hid_device_info* info = hid_get_device_info(dev);
unsigned short pid = (info) ? info->product_id : 0;
obuf[0x00] = 0;
obuf[0x01] = APEX_2023_PACKET_ID_REGION;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2);
memset(obuf, 0x00, sizeof(obuf));
if(pid == 0x1642)
if(result > 3 && ibuf[0] == APEX_2023_PACKET_ID_REGION)
{
obuf[0x00] = 0;
obuf[0x01] = 0xF5;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2);
if(result > 3 && ibuf[0] == 0xF5)
switch(ibuf[2])
{
switch(ibuf[2])
{
case 0x1:
return "64740";
break;
case 0x3:
return "64741";
break;
case 0x4:
return "64743";
break;
case 0x6:
return "64744";
break;
case 0xA:
return "64742";
break;
case 0xD:
return "64745";
break;
default:
break;
}
case 0x1:
return "64740";
break;
case 0x3:
return "64741";
break;
case 0x4:
return "64743";
break;
case 0x6:
return "64744";
break;
case 0xA:
return "64742";
break;
case 0xD:
return "64745";
break;
default:
break;
}
}
return "64865";
}
return SteelSeriesApexBaseController::GetSerial();
if(device_pid == STEELSERIES_APEX_9_TKL_PID)
{
return "64847";
}
if(device_pid == STEELSERIES_APEX_9_MINI_PID)
{
return "64837";
}
return "64865";
}
@@ -1,5 +1,5 @@
/*---------------------------------------------------------*\
| SteelSeriesApexController.cpp |
| SteelSeriesApexController.h |
| |
| Driver for SteelSeries Apex Keyboards |
| |
@@ -18,42 +18,22 @@
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#include "SteelSeriesApexBaseController.h"
enum
{
APEX_PACKET_ID_DIRECT = 0x3a, /* Direct mode */
APEX_2023_PACKET_ID_DIRECT = 0x40, /* New Wired Direct mode */
APEX_2023_PACKET_ID_DIRECT_WIRELESS = 0x61, /* New Wireless Direct mode */
APEX_2023_PACKET_ID_INIT = 0x4B, /* New Initialization */
APEX_2023_PACKET_LENGTH = 643,
APEX_GEN3_PACKET_CLEAR_LIGHTING = 0x41,
};
class SteelSeriesApexController : public SteelSeriesApexBaseController
{
public:
SteelSeriesApexController(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name);
SteelSeriesApexController(hid_device* dev_handle, steelseries_type type, const char* path, unsigned short pid, std::string dev_name);
~SteelSeriesApexController();
void SetMode
(
unsigned char mode,
std::vector<RGBColor> colors
);
void SetLEDsDirect(std::vector<RGBColor> colors);
void SetMode(unsigned char mode, std::vector<RGBColor> colors) override;
void SetLEDsDirect(std::vector<RGBColor> colors) override;
std::string GetSerial() override;
private:
void SelectProfile(unsigned char profile);
void SendInitialization();
void SendDeinitialization();
void SelectProfile
(
unsigned char profile
);
void SendInitialization();
unsigned int direct_packet_length;
};
@@ -14,7 +14,7 @@
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#include "SteelSeriesApexBaseController.h"
class SteelSeriesApexMController : public SteelSeriesApexBaseController
@@ -14,7 +14,7 @@
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#include "SteelSeriesApex3Controller.h"
#define STEELSERIES_TZ_LED_COUNT 10
@@ -0,0 +1,174 @@
/*---------------------------------------------------------*\
| RGBController_SteelSeriesArctisNova3.cpp |
| |
| RGBController for SteelSeries Arctis Nova 3 |
| |
| Tomasz Gorczyca (eldiablo123) 18 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_SteelSeriesArctisNova3.h"
/**------------------------------------------------------------------*\
@name Steelseries Arctis Nova 3
@category Headset
@type USB
@save :white_check_mark:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectSteelSeriesArctisNova3
@comment
\*-------------------------------------------------------------------*/
RGBController_SteelSeriesArctisNova3::RGBController_SteelSeriesArctisNova3(SteelSeriesArctisNova3Controller* controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "SteelSeries";
type = DEVICE_TYPE_HEADSET;
description = "SteelSeries Arctis Nova 3 Headset Device";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
mode Direct;
Direct.name = "Direct";
Direct.value = ARCTIS_NOVA3_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE;
Direct.color_mode = MODE_COLORS_PER_LED;
mode Off;
Off.name = "Off";
Off.value = ARCTIS_NOVA3_MODE_OFF;
Off.flags = 0;
Off.color_mode = MODE_COLORS_NONE;
mode Breathe;
Breathe.name = "Breathe";
Breathe.value = ARCTIS_NOVA3_MODE_BREATHE;
Breathe.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE;
Breathe.color_mode = MODE_COLORS_PER_LED;
Breathe.speed_min = 1;
Breathe.speed_max = 10;
Breathe.speed = 9;
mode ColorShiftRainbow;
ColorShiftRainbow.name = "Color Shift - Rainbow";
ColorShiftRainbow.value = ARCTIS_NOVA3_MODE_COLORSHIFT_RAINBOW;
ColorShiftRainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE;
ColorShiftRainbow.color_mode = MODE_COLORS_NONE;
ColorShiftRainbow.speed_min = 1;
ColorShiftRainbow.speed_max = 10;
ColorShiftRainbow.speed = 6;
mode ColorShiftOrange;
ColorShiftOrange.name = "Color Shift - Heat Orange";
ColorShiftOrange.value = ARCTIS_NOVA3_MODE_COLORSHIFT_ORANGE;
ColorShiftOrange.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE;
ColorShiftOrange.color_mode = MODE_COLORS_NONE;
ColorShiftOrange.speed_min = 1;
ColorShiftOrange.speed_max = 10;
ColorShiftOrange.speed = 6;
mode ColorShiftBlue;
ColorShiftBlue.name = "Color Shift - Frost Blue";
ColorShiftBlue.value = ARCTIS_NOVA3_MODE_COLORSHIFT_BLUE;
ColorShiftBlue.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE;
ColorShiftBlue.color_mode = MODE_COLORS_NONE;
ColorShiftBlue.speed_min = 1;
ColorShiftBlue.speed_max = 10;
ColorShiftBlue.speed = 6;
modes.push_back(Direct);
modes.push_back(Off);
modes.push_back(Breathe);
modes.push_back(ColorShiftRainbow);
modes.push_back(ColorShiftOrange);
modes.push_back(ColorShiftBlue);
SetupZones();
}
RGBController_SteelSeriesArctisNova3::~RGBController_SteelSeriesArctisNova3()
{
delete controller;
}
void RGBController_SteelSeriesArctisNova3::SetupZones()
{
zone zone1;
zone1.name = "Nova 3";
zone1.type = ZONE_TYPE_LINEAR;
zone1.leds_min = 2;
zone1.leds_max = 2;
zone1.leds_count = 2;
zones.push_back(zone1);
led right_led;
right_led.name = "Right";
leds.push_back(right_led);
led left_led;
left_led.name = "Left";
leds.push_back(left_led);
SetupColors();
}
void RGBController_SteelSeriesArctisNova3::DeviceUpdateLEDs()
{
for(unsigned int i = 0; i < leds.size(); i++)
{
DeviceUpdateSingleLED(i);
}
}
void RGBController_SteelSeriesArctisNova3::DeviceUpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_SteelSeriesArctisNova3::DeviceUpdateSingleLED(int led)
{
unsigned char zone_id = (unsigned char)led;
unsigned char speed = (unsigned char)modes[active_mode].speed;
switch(modes[active_mode].value)
{
case ARCTIS_NOVA3_MODE_OFF:
controller->SetOff(zone_id);
break;
case ARCTIS_NOVA3_MODE_DIRECT:
controller->SetDirect(zone_id, colors[led]);
break;
case ARCTIS_NOVA3_MODE_BREATHE:
controller->SetBreathe(zone_id, colors[led], speed);
break;
case ARCTIS_NOVA3_MODE_COLORSHIFT_RAINBOW:
controller->SetColorShiftRainbow(zone_id, speed);
break;
case ARCTIS_NOVA3_MODE_COLORSHIFT_ORANGE:
controller->SetColorShiftOrange(zone_id, speed);
break;
case ARCTIS_NOVA3_MODE_COLORSHIFT_BLUE:
controller->SetColorShiftBlue(zone_id, speed);
break;
}
}
void RGBController_SteelSeriesArctisNova3::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
void RGBController_SteelSeriesArctisNova3::DeviceSaveMode()
{
controller->Save(0);
controller->Save(1);
}
@@ -0,0 +1,33 @@
/*---------------------------------------------------------*\
| RGBController_SteelSeriesArctisNova3.h |
| |
| RGBController for SteelSeries Arctis Nova 3 |
| |
| Tomasz Gorczyca (eldiablo123) 18 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "SteelSeriesArctisNova3Controller.h"
class RGBController_SteelSeriesArctisNova3 : public RGBController
{
public:
RGBController_SteelSeriesArctisNova3(SteelSeriesArctisNova3Controller* controller_ptr);
~RGBController_SteelSeriesArctisNova3();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
SteelSeriesArctisNova3Controller* controller;
};
@@ -0,0 +1,284 @@
/*---------------------------------------------------------*\
| SteelSeriesArctisNova3Controller.cpp |
| |
| Driver for SteelSeries Arctis Nova 3 |
| |
| Tomasz Gorczyca (eldiablo123) 18 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <string.h>
#include "SteelSeriesArctisNova3Controller.h"
#include "StringUtils.h"
SteelSeriesArctisNova3Controller::SteelSeriesArctisNova3Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name)
{
dev = dev_handle;
location = info.path;
name = dev_name;
}
SteelSeriesArctisNova3Controller::~SteelSeriesArctisNova3Controller()
{
hid_close(dev);
}
std::string SteelSeriesArctisNova3Controller::GetDeviceLocation()
{
return("HID: " + location);
}
std::string SteelSeriesArctisNova3Controller::GetNameString()
{
return(name);
}
std::string SteelSeriesArctisNova3Controller::GetSerialString()
{
wchar_t serial_string[128];
int ret = hid_get_serial_number_string(dev, serial_string, 128);
if(ret != 0)
{
return("");
}
return(StringUtils::wstring_to_string(serial_string));
}
void SteelSeriesArctisNova3Controller::SetSpeedBytes(unsigned char* usb_buf, unsigned char speed)
{
switch(speed)
{
case 1: usb_buf[0x0D] = 0x30; usb_buf[0x0E] = 0x75; break;
case 2: usb_buf[0x0D] = 0x2E; usb_buf[0x0E] = 0x68; break;
case 3: usb_buf[0x0D] = 0x2C; usb_buf[0x0E] = 0x5B; break;
case 4: usb_buf[0x0D] = 0x20; usb_buf[0x0E] = 0x4E; break;
case 5: usb_buf[0x0D] = 0x1E; usb_buf[0x0E] = 0x41; break;
case 6: usb_buf[0x0D] = 0x1C; usb_buf[0x0E] = 0x34; break;
case 7: usb_buf[0x0D] = 0x1A; usb_buf[0x0E] = 0x27; break;
case 8: usb_buf[0x0D] = 0x0E; usb_buf[0x0E] = 0x1A; break;
case 9: usb_buf[0x0D] = 0x0C; usb_buf[0x0E] = 0x0D; break;
case 10: usb_buf[0x0D] = 0x94; usb_buf[0x0E] = 0x02; break;
default: usb_buf[0x0D] = 0x0C; usb_buf[0x0E] = 0x0D; break;
}
}
/*---------------------------------------------------------------------*\
| Select/enable packet (64 bytes): 06 A5 (01=left/02=right) (00/0A) |
\*---------------------------------------------------------------------*/
void SteelSeriesArctisNova3Controller::SelectZone(unsigned char zone_id, unsigned char state)
{
unsigned char usb_buf[ARCTIS_NOVA3_APPLY_REPORT_SIZE];
memset(usb_buf, 0x00, ARCTIS_NOVA3_APPLY_REPORT_SIZE);
usb_buf[0x00] = ARCTIS_NOVA3_REPORT_ID;
usb_buf[0x01] = 0xA5;
usb_buf[0x02] = (zone_id == 0) ? 0x02 : 0x01;
usb_buf[0x03] = state;
hid_write(dev, usb_buf, ARCTIS_NOVA3_APPLY_REPORT_SIZE);
}
/*---------------------------------------------------------------------*\
| Apply packet (64 bytes): 06 (A3=left/09=right) |
\*---------------------------------------------------------------------*/
void SteelSeriesArctisNova3Controller::ApplyZone(unsigned char zone_id)
{
unsigned char usb_buf[ARCTIS_NOVA3_APPLY_REPORT_SIZE];
memset(usb_buf, 0x00, ARCTIS_NOVA3_APPLY_REPORT_SIZE);
usb_buf[0x00] = ARCTIS_NOVA3_REPORT_ID;
usb_buf[0x01] = (zone_id == 0) ? 0x09 : 0xA3;
hid_write(dev, usb_buf, ARCTIS_NOVA3_APPLY_REPORT_SIZE);
}
void SteelSeriesArctisNova3Controller::SendEffectPacket(unsigned char* usb_buf)
{
hid_write(dev, usb_buf, ARCTIS_NOVA3_REPORT_SIZE);
}
void SteelSeriesArctisNova3Controller::SetOff(unsigned char zone_id)
{
SelectZone(zone_id, 0x00);
ApplyZone(zone_id);
}
/*---------------------------------------------------------------------*\
| Save |
\*---------------------------------------------------------------------*/
void SteelSeriesArctisNova3Controller::Save(unsigned char zone_id)
{
ApplyZone(zone_id);
}
/*---------------------------------------------------------------------*\
| Single Color / Direct |
\*---------------------------------------------------------------------*/
void SteelSeriesArctisNova3Controller::SetDirect(unsigned char zone_id, RGBColor color)
{
unsigned char usb_buf[ARCTIS_NOVA3_REPORT_SIZE];
memset(usb_buf, 0x00, ARCTIS_NOVA3_REPORT_SIZE);
usb_buf[0x00] = ARCTIS_NOVA3_REPORT_ID;
usb_buf[0x01] = 0xAA;
usb_buf[0x02] = (zone_id == 0) ? 0x01 : 0x00;
usb_buf[0x0D] = 0x98;
usb_buf[0x0E] = 0x3A;
usb_buf[0x15] = 0x55;
usb_buf[0x17] = 0x96;
usb_buf[0x1D] = 0x01;
usb_buf[0x25] = 0x02;
usb_buf[0x2D] = RGBGetRValue(color);
usb_buf[0x2E] = RGBGetGValue(color);
usb_buf[0x2F] = RGBGetBValue(color);
usb_buf[0x30] = 0xFF;
SendEffectPacket(usb_buf);
SelectZone(zone_id, 0x0A);
ApplyZone(zone_id);
}
/*---------------------------------------------------------------------*\
| Breathe |
\*---------------------------------------------------------------------*/
void SteelSeriesArctisNova3Controller::SetBreathe(unsigned char zone_id, RGBColor color, unsigned char speed)
{
unsigned char usb_buf[ARCTIS_NOVA3_REPORT_SIZE];
memset(usb_buf, 0x00, ARCTIS_NOVA3_REPORT_SIZE);
usb_buf[0x00] = ARCTIS_NOVA3_REPORT_ID;
usb_buf[0x01] = 0xAA;
usb_buf[0x02] = (zone_id == 0) ? 0x01 : 0x00;
SetSpeedBytes(usb_buf, speed);
usb_buf[0x15] = 0x55;
usb_buf[0x17] = 0x96;
usb_buf[0x25] = 0x03;
usb_buf[0x29] = RGBGetRValue(color);
usb_buf[0x2A] = RGBGetGValue(color);
usb_buf[0x2B] = RGBGetBValue(color);
usb_buf[0x30] = 0x7F;
usb_buf[0x31] = RGBGetRValue(color);
usb_buf[0x32] = RGBGetGValue(color);
usb_buf[0x33] = RGBGetBValue(color);
usb_buf[0x34] = 0x7F;
SendEffectPacket(usb_buf);
SelectZone(zone_id, 0x0A);
ApplyZone(zone_id);
}
/*---------------------------------------------------------------------*\
| Color Shift - Rainbow |
\*---------------------------------------------------------------------*/
void SteelSeriesArctisNova3Controller::SetColorShiftRainbow(unsigned char zone_id, unsigned char speed)
{
unsigned char usb_buf[ARCTIS_NOVA3_REPORT_SIZE];
memset(usb_buf, 0x00, ARCTIS_NOVA3_REPORT_SIZE);
usb_buf[0x00] = ARCTIS_NOVA3_REPORT_ID;
usb_buf[0x01] = 0xAA;
usb_buf[0x02] = (zone_id == 0) ? 0x01 : 0x00;
SetSpeedBytes(usb_buf, speed);
usb_buf[0x15] = 0x55;
usb_buf[0x17] = 0x96;
usb_buf[0x25] = 0x07;
static const unsigned char rainbow_stops[] =
{
0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
0x2B, 0x00, 0xFF, 0x00, 0x2B, 0x00, 0xFF, 0xFF, 0x2B, 0x00,
0x00, 0xFF, 0x2B, 0xFF, 0x00, 0xFF, 0x2B, 0xFF, 0x00, 0x00,
0x26
};
memcpy(&usb_buf[0x26], rainbow_stops, sizeof(rainbow_stops));
SendEffectPacket(usb_buf);
SelectZone(zone_id, 0x0A);
ApplyZone(zone_id);
}
/*---------------------------------------------------------------------*\
| Color Shift - Heat Orange |
\*---------------------------------------------------------------------*/
void SteelSeriesArctisNova3Controller::SetColorShiftOrange(unsigned char zone_id, unsigned char speed)
{
unsigned char usb_buf[ARCTIS_NOVA3_REPORT_SIZE];
memset(usb_buf, 0x00, ARCTIS_NOVA3_REPORT_SIZE);
usb_buf[0x00] = ARCTIS_NOVA3_REPORT_ID;
usb_buf[0x01] = 0xAA;
usb_buf[0x02] = (zone_id == 0) ? 0x01 : 0x00;
SetSpeedBytes(usb_buf, speed);
usb_buf[0x15] = 0x55;
usb_buf[0x17] = 0x96;
usb_buf[0x25] = 0x03;
static const unsigned char orange_stops[] =
{
0xFF, 0xEA, 0x00, 0xFF, 0xEA, 0x00, 0x00, 0xFF, 0x4D, 0x00,
0x7F, 0xFF, 0xEA, 0x00, 0x7F
};
memcpy(&usb_buf[0x26], orange_stops, sizeof(orange_stops));
SendEffectPacket(usb_buf);
SelectZone(zone_id, 0x0A);
ApplyZone(zone_id);
}
/*---------------------------------------------------------------------*\
| Color Shift - Frost Blue |
\*---------------------------------------------------------------------*/
void SteelSeriesArctisNova3Controller::SetColorShiftBlue(unsigned char zone_id, unsigned char speed)
{
unsigned char usb_buf[ARCTIS_NOVA3_REPORT_SIZE];
memset(usb_buf, 0x00, ARCTIS_NOVA3_REPORT_SIZE);
usb_buf[0x00] = ARCTIS_NOVA3_REPORT_ID;
usb_buf[0x01] = 0xAA;
usb_buf[0x02] = (zone_id == 0) ? 0x01 : 0x00;
SetSpeedBytes(usb_buf, speed);
usb_buf[0x15] = 0x55;
usb_buf[0x17] = 0x96;
usb_buf[0x25] = 0x03;
static const unsigned char blue_stops[] =
{
0x8C, 0x00, 0xFF, 0x8C, 0x00, 0xFF, 0x00, 0x3B, 0xD1, 0xFF,
0x7F, 0x8C, 0x00, 0xFF, 0x7F
};
memcpy(&usb_buf[0x26], blue_stops, sizeof(blue_stops));
SendEffectPacket(usb_buf);
SelectZone(zone_id, 0x0A);
ApplyZone(zone_id);
}
@@ -0,0 +1,58 @@
/*---------------------------------------------------------*\
| SteelSeriesArctisNova3Controller.h |
| |
| Driver for SteelSeries Arctis Nova 3 |
| |
| Tomasz Gorczyca (eldiablo123) 18 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#define ARCTIS_NOVA3_REPORT_SIZE 521
#define ARCTIS_NOVA3_APPLY_REPORT_SIZE 64
#define ARCTIS_NOVA3_REPORT_ID 0x06
enum
{
ARCTIS_NOVA3_MODE_OFF = 0x00,
ARCTIS_NOVA3_MODE_DIRECT = 0x01,
ARCTIS_NOVA3_MODE_BREATHE = 0x02,
ARCTIS_NOVA3_MODE_COLORSHIFT_RAINBOW = 0x03,
ARCTIS_NOVA3_MODE_COLORSHIFT_ORANGE = 0x04,
ARCTIS_NOVA3_MODE_COLORSHIFT_BLUE = 0x05,
};
class SteelSeriesArctisNova3Controller
{
public:
SteelSeriesArctisNova3Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name);
~SteelSeriesArctisNova3Controller();
std::string GetDeviceLocation();
std::string GetNameString();
std::string GetSerialString();
void SetOff(unsigned char zone_id);
void Save(unsigned char zone_id);
void SetDirect(unsigned char zone_id, RGBColor color);
void SetBreathe(unsigned char zone_id, RGBColor color, unsigned char speed);
void SetColorShiftRainbow(unsigned char zone_id, unsigned char speed);
void SetColorShiftOrange(unsigned char zone_id, unsigned char speed);
void SetColorShiftBlue(unsigned char zone_id, unsigned char speed);
private:
hid_device* dev;
std::string location;
std::string name;
void SetSpeedBytes(unsigned char* usb_buf, unsigned char speed);
void SelectZone(unsigned char zone_id, unsigned char state);
void ApplyZone(unsigned char zone_id);
void SendEffectPacket(unsigned char* usb_buf);
};
@@ -9,12 +9,12 @@
#include <hidapi.h>
#include "DetectionManager.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#include "SteelSeriesAeroxWirelessController.h"
#include "SteelSeriesAerox5Controller.h"
#include "SteelSeriesArctisNova3Controller.h"
#include "SteelSeriesArctis5Controller.h"
#include "SteelSeriesApex8ZoneController.h"
#include "SteelSeriesApex9Controller.h"
#include "SteelSeriesApexController.h"
#include "SteelSeriesApexMController.h"
#include "SteelSeriesApexTZoneController.h"
@@ -24,6 +24,7 @@
#include "SteelSeriesRival3Controller.h"
#include "SteelSeriesSenseiController.h"
#include "SteelSeriesSiberiaController.h"
#include "RGBController_SteelSeriesArctisNova3.h"
#include "RGBController_SteelSeriesArctis5.h"
#include "RGBController_SteelSeriesApex.h"
#include "RGBController_SteelSeriesApex3.h"
@@ -34,99 +35,7 @@
#include "RGBController_SteelSeriesSensei.h"
#include "RGBController_SteelSeriesSiberia.h"
/*---------------------------------------------------------*\
| Vendor ID |
\*---------------------------------------------------------*/
#define STEELSERIES_VID 0x1038
/*---------------------------------------------------------*\
| Mouse product IDs |
\*---------------------------------------------------------*/
#define STEELSERIES_AEROX_3_PID 0x1836
#define STEELSERIES_AEROX_3_WIRELESS_PID 0x1838
#define STEELSERIES_AEROX_3_WIRELESS_WIRED_PID 0x183A
#define STEELSERIES_AEROX_3_CS2_WIRELESS_PID 0x1878
#define STEELSERIES_AEROX_3_CS2_WIRELESS_WIRED_PID 0x187A
#define STEELSERIES_AEROX_5_WIRELESS_PID 0x1852
#define STEELSERIES_AEROX_5_WIRELESS_WIRED_PID 0x1854
#define STEELSERIES_AEROX_5_DESTINY_WIRELESS_PID 0x185C
#define STEELSERIES_AEROX_5_DESTINY_WIRELESS_WIRED_PID 0x185E
#define STEELSERIES_AEROX_5_DIABLO_WIRELESS_PID 0x1860
#define STEELSERIES_AEROX_5_DIABLO_WIRELESS_WIRED_PID 0x1862
#define STEELSERIES_AEROX_9_WIRELESS_PID 0x1858
#define STEELSERIES_AEROX_9_WIRELESS_WIRED_PID 0x185A
#define STEELSERIES_AEROX_5_PID 0x1850
#define STEELSERIES_AEROX_9_PID 0x185A
#define STEELSERIES_RIVAL_100_PID 0x1702
#define STEELSERIES_RIVAL_100_DOTA_PID 0x170C
#define STEELSERIES_RIVAL_105_PID 0x1814
#define STEELSERIES_RIVAL_106_PID 0x1816
#define STEELSERIES_RIVAL_110_PID 0x1729
#define STEELSERIES_RIVAL_300_PID 0x1710
#define ACER_PREDATOR_RIVAL_300_PID 0x1714
#define STEELSERIES_RIVAL_300_CSGO_PID 0x1394
#define STEELSERIES_RIVAL_300_CSGO_STM32_PID 0x1716
#define STEELSERIES_RIVAL_300_CSGO_HYPERBEAST_PID 0x171A
#define STEELSERIES_RIVAL_300_DOTA_PID 0x1392
#define STEELSERIES_RIVAL_300_HP_PID 0x1718
#define STEELSERIES_RIVAL_300_BLACKOPS_PID 0x1710
#define STEELSERIES_RIVAL_310_PID 0x1720
#define STEELSERIES_RIVAL_310_CSGO_HOWL_PID 0x171E
#define STEELSERIES_RIVAL_310_PUBG_PID 0x1736
#define STEELSERIES_RIVAL_600_PID 0x1724
#define STEELSERIES_RIVAL_600_DOTA_2_PID 0x172E
#define STEELSERIES_RIVAL_650_PID 0x172B
#define STEELSERIES_RIVAL_650_WIRELESS_PID 0x1726
#define STEELSERIES_RIVAL_700_PID 0x1700
#define STEELSERIES_RIVAL_710_PID 0x1730
#define STEELSERIES_RIVAL_3_OLD_PID 0x1824
#define STEELSERIES_RIVAL_3_PID 0x184C
#define STEELSERIES_SENSEI_TEN_PID 0x1832
#define STEELSERIES_SENSEI_TEN_CSGO_NEON_RIDER_PID 0x1834
#define STEELSERIES_SENSEI_310_PID 0x1722
/*---------------------------------------------------------*\
| Headset product IDs |
\*---------------------------------------------------------*/
#define STEELSERIES_SIBERIA_350_PID 0x1229
#define STEELSERIES_ARCTIS_5_PID 0x1250
#define STEELSERIES_ARCTIS_5_V2_PID 0x12AA
/*--------------------------------------------------------------------*\
| Mousemat product IDs |
\*--------------------------------------------------------------------*/
#define STEELSERIES_QCK_PRISM_CLOTH_MED_PID 0x150A
#define STEELSERIES_QCK_PRISM_CLOTH_XL_PID 0x150D
#define STEELSERIES_QCK_PRISM_CLOTH_XL_DESTINY_PID 0x151E
#define STEELSERIES_QCK_PRISM_CLOTH_XL_CSGO_NEON_RIDER_PID 0x1514
#define STEELSERIES_QCK_PRISM_CLOTH_XL_CSGO_NEO_NOIR_PID 0x151C
#define STEELSERIES_QCK_PRISM_CLOTH_3XL_PID 0x1516
#define STEELSERIES_QCK_PRISM_CLOTH_4XL_PID 0x1518
#define STEELSERIES_QCK_PRISM_CLOTH_5XL_PID 0x151A
#define STEELSERIES_QCK_PRISM_CLOTH_XL_DESTINY_2_LIGHTFALL_ED_PID 0x1520
/*---------------------------------------------------------*\
| Keyboard product IDs |
\*---------------------------------------------------------*/
#define STEELSERIES_APEX_3_PID 0x161A
#define STEELSERIES_APEX_3_TKL_PID 0x1622
#define STEELSERIES_APEX_5_PID 0x161C
#define STEELSERIES_APEX_7_PID 0x1612
#define STEELSERIES_APEX_7_TKL_PID 0x1618
#define STEELSERIES_APEX_9_TKL_PID 0x1634
#define STEELSERIES_APEX_9_MINI_PID 0x1620
#define STEELSERIES_APEX_PRO_PID 0x1610
#define STEELSERIES_APEX_PRO_TKL_PID 0x1614
#define STEELSERIES_APEX_PRO_TKL_2023_PID 0x1628
#define STEELSERIES_APEX_PRO_TKL_2023_WL_PID_1 0x1630
#define STEELSERIES_APEX_PRO_TKL_2023_WL_PID_2 0x1632
#define STEELSERIES_APEX_PRO_TKL_GEN3_PID 0x1642
#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1 0x1644
#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2 0x1646
#define STEELSERIES_APEX_M750_PID 0x0616
#define STEELSERIES_APEX_OG_PID 0x1202
#define STEELSERIES_APEX_350_PID 0x1206
#define STEELSERIES_APEX_PRO3_PID 0x1640
DetectedControllers DetectSteelSeriesAerox3(hid_device_info* info, const std::string& name)
{
@@ -277,7 +186,7 @@ DetectedControllers DetectSteelSeriesApex(hid_device_info* info, const std::stri
if(dev)
{
SteelSeriesApexController* controller = new SteelSeriesApexController(dev, APEX, info->path, name);
SteelSeriesApexController* controller = new SteelSeriesApexController(dev, APEX, info->path, info->product_id, name);
RGBController_SteelSeriesApex* rgb_controller = new RGBController_SteelSeriesApex(controller);
detected_controllers.push_back(rgb_controller);
@@ -286,34 +195,6 @@ DetectedControllers DetectSteelSeriesApex(hid_device_info* info, const std::stri
return(detected_controllers);
}
DetectedControllers DetectSteelSeriesApex9(hid_device_info* info, const std::string& name, steelseries_type proto_type)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
SteelSeriesApex9Controller* controller = new SteelSeriesApex9Controller(dev, proto_type, info->path, name);
RGBController_SteelSeriesApex* rgb_controller = new RGBController_SteelSeriesApex(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectSteelSeriesApex9TKL(hid_device_info* info, const std::string& name)
{
return(DetectSteelSeriesApex9(info, name, APEX_9_TKL));
}
DetectedControllers DetectSteelSeriesApex9Mini(hid_device_info* info, const std::string& name)
{
return(DetectSteelSeriesApex9(info, name, APEX_9_MINI));
}
DetectedControllers DetectSteelSeriesApexM(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
@@ -513,6 +394,24 @@ DetectedControllers DetectSteelSeriesSensei(hid_device_info* info, const std::st
return(detected_controllers);
}
DetectedControllers DetectSteelSeriesArctisNova3(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
SteelSeriesArctisNova3Controller* controller = new SteelSeriesArctisNova3Controller(dev, *info, name);
RGBController_SteelSeriesArctisNova3* rgb_controller = new RGBController_SteelSeriesArctisNova3(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectSteelSeriesArctis5(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
@@ -580,6 +479,7 @@ REGISTER_HID_DETECTOR_I("SteelSeries Sensei 310",
| Headsets |
\*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
REGISTER_HID_DETECTOR_I("SteelSeries Siberia 350", DetectSteelSeriesHeadset, STEELSERIES_VID, STEELSERIES_SIBERIA_350_PID, 3 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Arctis Nova 3", DetectSteelSeriesArctisNova3, STEELSERIES_VID, STEELSERIES_ARCTIS_NOVA_3_PID, 4, 0xFFC0, 1);
REGISTER_HID_DETECTOR_I("SteelSeries Arctis 5", DetectSteelSeriesArctis5, STEELSERIES_VID, STEELSERIES_ARCTIS_5_PID, 5 );
REGISTER_HID_DETECTOR_I("SteelSeries Arctis 5", DetectSteelSeriesArctis5, STEELSERIES_VID, STEELSERIES_ARCTIS_5_V2_PID, 5 );
@@ -604,8 +504,8 @@ REGISTER_HID_DETECTOR_IPU("SteelSeries Apex 3 TKL", Dete
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 5", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_5_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 7", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_7_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 7 TKL", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_7_TKL_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 9 TKL", DetectSteelSeriesApex9TKL, STEELSERIES_VID, STEELSERIES_APEX_9_TKL_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 9 Mini", DetectSteelSeriesApex9Mini, STEELSERIES_VID, STEELSERIES_APEX_9_MINI_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 9 TKL", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_9_TKL_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 9 Mini", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_9_MINI_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL 2023 Wired", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_PID, 1 );
@@ -0,0 +1,138 @@
/*---------------------------------------------------------*\
| SteelSeriesDevices.h |
| |
| Device list for SteelSeries devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
/*---------------------------------------------------------*\
| Vendor ID |
\*---------------------------------------------------------*/
#define STEELSERIES_VID 0x1038
/*---------------------------------------------------------*\
| Mouse product IDs |
\*---------------------------------------------------------*/
#define STEELSERIES_AEROX_3_PID 0x1836
#define STEELSERIES_AEROX_3_WIRELESS_PID 0x1838
#define STEELSERIES_AEROX_3_WIRELESS_WIRED_PID 0x183A
#define STEELSERIES_AEROX_3_CS2_WIRELESS_PID 0x1878
#define STEELSERIES_AEROX_3_CS2_WIRELESS_WIRED_PID 0x187A
#define STEELSERIES_AEROX_5_WIRELESS_PID 0x1852
#define STEELSERIES_AEROX_5_WIRELESS_WIRED_PID 0x1854
#define STEELSERIES_AEROX_5_DESTINY_WIRELESS_PID 0x185C
#define STEELSERIES_AEROX_5_DESTINY_WIRELESS_WIRED_PID 0x185E
#define STEELSERIES_AEROX_5_DIABLO_WIRELESS_PID 0x1860
#define STEELSERIES_AEROX_5_DIABLO_WIRELESS_WIRED_PID 0x1862
#define STEELSERIES_AEROX_9_WIRELESS_PID 0x1858
#define STEELSERIES_AEROX_9_WIRELESS_WIRED_PID 0x185A
#define STEELSERIES_AEROX_5_PID 0x1850
#define STEELSERIES_AEROX_9_PID 0x185A
#define STEELSERIES_RIVAL_100_PID 0x1702
#define STEELSERIES_RIVAL_100_DOTA_PID 0x170C
#define STEELSERIES_RIVAL_105_PID 0x1814
#define STEELSERIES_RIVAL_106_PID 0x1816
#define STEELSERIES_RIVAL_110_PID 0x1729
#define STEELSERIES_RIVAL_300_PID 0x1710
#define ACER_PREDATOR_RIVAL_300_PID 0x1714
#define STEELSERIES_RIVAL_300_CSGO_PID 0x1394
#define STEELSERIES_RIVAL_300_CSGO_STM32_PID 0x1716
#define STEELSERIES_RIVAL_300_CSGO_HYPERBEAST_PID 0x171A
#define STEELSERIES_RIVAL_300_DOTA_PID 0x1392
#define STEELSERIES_RIVAL_300_HP_PID 0x1718
#define STEELSERIES_RIVAL_300_BLACKOPS_PID 0x1710
#define STEELSERIES_RIVAL_310_PID 0x1720
#define STEELSERIES_RIVAL_310_CSGO_HOWL_PID 0x171E
#define STEELSERIES_RIVAL_310_PUBG_PID 0x1736
#define STEELSERIES_RIVAL_600_PID 0x1724
#define STEELSERIES_RIVAL_600_DOTA_2_PID 0x172E
#define STEELSERIES_RIVAL_650_PID 0x172B
#define STEELSERIES_RIVAL_650_WIRELESS_PID 0x1726
#define STEELSERIES_RIVAL_700_PID 0x1700
#define STEELSERIES_RIVAL_710_PID 0x1730
#define STEELSERIES_RIVAL_3_OLD_PID 0x1824
#define STEELSERIES_RIVAL_3_PID 0x184C
#define STEELSERIES_SENSEI_TEN_PID 0x1832
#define STEELSERIES_SENSEI_TEN_CSGO_NEON_RIDER_PID 0x1834
#define STEELSERIES_SENSEI_310_PID 0x1722
/*---------------------------------------------------------*\
| Headset product IDs |
\*---------------------------------------------------------*/
#define STEELSERIES_SIBERIA_350_PID 0x1229
#define STEELSERIES_ARCTIS_NOVA_3_PID 0x12EC
#define STEELSERIES_ARCTIS_5_PID 0x1250
#define STEELSERIES_ARCTIS_5_V2_PID 0x12AA
/*--------------------------------------------------------------------*\
| Mousemat product IDs |
\*--------------------------------------------------------------------*/
#define STEELSERIES_QCK_PRISM_CLOTH_MED_PID 0x150A
#define STEELSERIES_QCK_PRISM_CLOTH_XL_PID 0x150D
#define STEELSERIES_QCK_PRISM_CLOTH_XL_DESTINY_PID 0x151E
#define STEELSERIES_QCK_PRISM_CLOTH_XL_CSGO_NEON_RIDER_PID 0x1514
#define STEELSERIES_QCK_PRISM_CLOTH_XL_CSGO_NEO_NOIR_PID 0x151C
#define STEELSERIES_QCK_PRISM_CLOTH_3XL_PID 0x1516
#define STEELSERIES_QCK_PRISM_CLOTH_4XL_PID 0x1518
#define STEELSERIES_QCK_PRISM_CLOTH_5XL_PID 0x151A
#define STEELSERIES_QCK_PRISM_CLOTH_XL_DESTINY_2_LIGHTFALL_ED_PID 0x1520
/*---------------------------------------------------------*\
| Keyboard product IDs |
\*---------------------------------------------------------*/
#define STEELSERIES_APEX_3_PID 0x161A
#define STEELSERIES_APEX_3_TKL_PID 0x1622
#define STEELSERIES_APEX_5_PID 0x161C
#define STEELSERIES_APEX_7_PID 0x1612
#define STEELSERIES_APEX_7_TKL_PID 0x1618
#define STEELSERIES_APEX_9_TKL_PID 0x1634
#define STEELSERIES_APEX_9_MINI_PID 0x1620
#define STEELSERIES_APEX_PRO_PID 0x1610
#define STEELSERIES_APEX_PRO_TKL_PID 0x1614
#define STEELSERIES_APEX_PRO_TKL_2023_PID 0x1628
#define STEELSERIES_APEX_PRO_TKL_2023_WL_PID_1 0x1630
#define STEELSERIES_APEX_PRO_TKL_2023_WL_PID_2 0x1632
#define STEELSERIES_APEX_PRO_TKL_GEN3_PID 0x1642
#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1 0x1644
#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2 0x1646
#define STEELSERIES_APEX_M750_PID 0x0616
#define STEELSERIES_APEX_OG_PID 0x1202
#define STEELSERIES_APEX_350_PID 0x1206
#define STEELSERIES_APEX_PRO3_PID 0x1640
/*--------------------------------------------------------*\
| Allows us to handle variation in the protocol. |
| Defined in a single enum so we can keep the device_list |
| struct the same for every possible device. |
\*--------------------------------------------------------*/
typedef enum
{
RIVAL_100 = 0x00,
RIVAL_300 = 0x01,
RIVAL_650 = 0x02,
SIBERIA_350 = 0x03,
APEX = 0x04,
APEX_M = 0x05,
APEX_OLD = 0x06,
SENSEI = 0x07,
RIVAL_600 = 0x08,
RIVAL_3 = 0x09,
APEX_TZONE = 0x0A,
RIVAL_700 = 0x0B,
AEROX_3 = 0x0C,
APEX_8ZONE = 0x0D,
AEROX_3_WIRELESS = 0x0E,
AEROX_3_WIRELESS_WIRED = 0x0F,
AEROX_5_WIRELESS = 0x10,
AEROX_5_WIRELESS_WIRED = 0x11,
AEROX_5_DESTINY_WIRELESS = 0x12,
AEROX_5_DESTINY_WIRELESS_WIRED = 0x13,
AEROX_5_DIABLO_WIRELESS = 0x14,
AEROX_5_DIABLO_WIRELESS_WIRED = 0x15,
AEROX_9_WIRELESS = 0x16,
AEROX_9_WIRELESS_WIRED = 0x17,
} steelseries_type;
@@ -1,48 +0,0 @@
/*---------------------------------------------------------*\
| SteelSeriesGeneric.h |
| |
| Generic file for SteelSeries devices |
| |
| B Horn (bahorn) 17 May 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
/* Allows us to handle variation in the protocol.
* Defined in a single enum so we can keep the device_list struct the same
* for every possible device. */
typedef enum
{
RIVAL_100 = 0x00,
RIVAL_300 = 0x01,
RIVAL_650 = 0x02,
SIBERIA_350 = 0x03,
APEX = 0x04,
APEX_M = 0x05,
APEX_OLD = 0x06,
SENSEI = 0x07,
RIVAL_600 = 0x08,
RIVAL_3 = 0x09,
APEX_TZONE = 0x0A,
RIVAL_700 = 0x0B,
AEROX_3 = 0x0C,
APEX_8ZONE = 0x0D,
AEROX_3_WIRELESS = 0x0E,
AEROX_3_WIRELESS_WIRED = 0x0F,
AEROX_5_WIRELESS = 0x10,
AEROX_5_WIRELESS_WIRED = 0x11,
AEROX_5_DESTINY_WIRELESS = 0x12,
AEROX_5_DESTINY_WIRELESS_WIRED = 0x13,
AEROX_5_DIABLO_WIRELESS = 0x14,
AEROX_5_DIABLO_WIRELESS_WIRED = 0x15,
AEROX_9_WIRELESS = 0x16,
AEROX_9_WIRELESS_WIRED = 0x17,
APEX_9_TKL = 0x18,
APEX_9_MINI = 0x19,
} steelseries_type;
@@ -15,7 +15,7 @@
#include <string>
#include <vector>
#include <hidapi.h>
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
#define STEELSERIES_MOUSE_BRIGHTNESS_MAX 0x64
@@ -19,7 +19,7 @@
#include <string>
#include <hidapi.h>
#include "color32.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesDevices.h"
/* Mode, we then use these to set actual effect based on speed. */
enum

Some files were not shown because too many files have changed in this diff Show More