Compare commits

...
Author SHA1 Message Date
Adam Honse f15c3c1420 Add Redragon M921 2026-06-07 14:08:34 -05:00
Adam Honse aa67045330 Fix duplicate entries in SteelSeriesControllerDetect 2026-06-07 13:28:46 -05:00
Alexey Kopytko 736640f38d Add support for Zotac RTX 5090 SOLID OC 2026-06-07 13:21:29 -05:00
David Glushkov e9caf663bc Fix MSILaptopController ALC zone mapping and packet initialization 2026-06-07 13:09:19 -05:00
Daniel Clark 8afbea53de Update RGBFusion2USB Driver. 2026-06-07 01:13:03 -05:00
metroite c3d496ad5b Initial commit for SteelSeries Apex Pro TKL Gen 3 2026-06-07 01:09:30 -05:00
ilyaKhalidov88 06501f528d Add support for ASUS ROG Astral 5090 2026-06-07 00:46:27 -05:00
Jerry Fan f6a678c1a4 Add support for RealtekARGBController 2026-06-05 01:03:29 -05:00
Andy Herbert 30690d0ebd New device- MSI MAG272CQR monitor 2026-06-04 11:54:51 -05:00
Kyle Finter 7a830cddf9 Added new Govee devices H612F and H607C 2026-06-04 07:27:02 +00:00
Adam Honse 3dc1365d39 Add PowerShell implementation of build-msi for building MSI on Windows, add additional information about PawnIO and the OpenRGB system service to the installer. 2026-06-02 22:07:17 -05:00
Adam Honse b03cd94ae8 Fix incorrect firmware packet format and add timeouts to HID reads in ThermaltakeRiingController 2026-06-02 01:07:33 -05:00
Jefferson González bbf348d273 Add HyperX Eve 1800 keyboard support 2026-06-02 00:15:19 -05:00
David Glushkov dce48eb415 Add MSI Raider A18 HX A9WJG SteelSeries RGB 2026-06-02 00:05:18 -05:00
Ox HaK decf14635d Add Fnatic Streak65 support 2026-06-01 08:27:17 -05:00
Richard Harris db4f49bbba Implement Direct mode support for Roccat Vulcan TKL (non-Pro) 2026-05-31 12:38:57 -05:00
HorrorTroll 26824fcb6e Added support for Colorful iGame GeForce RTX 5060 Ultra W OC 2026-05-31 15:12:36 +07:00
Jerry Fan 8afad91b33 Add support for RealtekBridgeController 2026-05-29 07:46:22 -05:00
Adam Honse 69de0b13c4 Update PawnIO modules to 0.2.6 and enable Intel Skylake IMC SMBus module 2026-05-28 23:48:42 -05:00
alstruit 967153bc10 Add support for ASUS ROG Gladius III Core 2026-05-25 12:08:43 -05:00
JAO1988 971d9be1bd Added Colorful iGame RTX 5060TI 16GB Ultra W DUO Variant 2026-05-23 23:10:36 -05:00
Mark Willis 7a3faef886 Initial commit for ASUS TUF GeForce RTX 5070 Ti Gaming White OC 2026-05-22 12:25:59 -05:00
Purrple Kitten f67030fcc7 Add support for the Gigabyte AORUS RTX 5080 MASTER ICE 2026-05-18 07:30:37 -05:00
Alex Jawhari 7353e99449 Fix AMD GPU i2c bus PCI ID detection on Linux (sysfs path traversal) 2026-05-17 23:58:21 -05:00
JAO1988 91d9597c89 Added Support for ASUS ROG ASTRAL GeForce RTX 5080 WHITE 2026-05-17 23:47:55 -05:00
Adam Honse 2eb569912b XPGSummonerKeyboardController - Remove fake modes 2026-05-13 11:49:05 -05:00
Ken Sanislo 9410d9c066 Add support for Thrustmaster Sol series joystick RGB LEDs 2026-05-09 00:21:40 -05:00
Adam Honse 29d8788e25 Allow value 0x27 at address 0x00 for Galax GPU controllers at address 0x23 2026-05-08 20:05:40 -05:00
Elvis Pranskevichus 0304c50bc6 Add support for Gigabyte AORUS RX 7900 XTX ELITE 24G 2026-05-08 00:12:43 -05:00
69 changed files with 12760 additions and 3572 deletions
@@ -22,6 +22,7 @@
#define AURA_ROG_GLADIUS_II_WIRELESS_1_PID 0x189E
#define AURA_ROG_GLADIUS_II_WIRELESS_2_PID 0x18A0
#define AURA_ROG_GLADIUS_III_PID 0x197B
#define AURA_ROG_GLADIUS_III_CORE_PID 0x1C8D
#define AURA_ROG_GLADIUS_III_WIRELESS_USB_PID 0x197D
#define AURA_ROG_GLADIUS_III_WIRELESS_2_4_PID 0x197F
#define AURA_ROG_GLADIUS_III_WIRELESS_BT_PID 0x1981
@@ -217,6 +218,20 @@ static std::map<int,mouse_type> aura_mouse_devices =
{ AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY }
}
},
{
AURA_ROG_GLADIUS_III_CORE_PID, // ROG Gladius III Core
{
0,
0,
0,
100,
false,
1,
false,
{ AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL },
{ AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE }
}
},
{
AURA_ROG_GLADIUS_III_WIRELESS_USB_PID, // ROG Gladius III Wireless USB
{
@@ -394,6 +394,7 @@ REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Origin COD",
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Wireless", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_WIRELESS_1_PID, 1, 0xFF13);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Wireless", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_WIRELESS_2_PID, 2, 0xFF01);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_PID, 0, 0xFF01);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Core", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_CORE_PID, 0, 0xFF01);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless USB", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_USB_PID, 0, 0xFF01);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless 2.4Ghz", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_2_4_PID, 0, 0xFF01);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless Bluetooth", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_BT_PID, 0, 0xFF01);
@@ -1,4 +1,4 @@
/*---------------------------------------------------------*\
/*---------------------------------------------------------*\
| ColorfulGPUControllerDetect.cpp |
| |
| Detector for Colorful GPU |
@@ -48,7 +48,7 @@ REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Advanced OC 12G L-V", Detec
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA106_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3070_ADVANCED_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060TI_ADVANCED_OC, 0x61);
@@ -72,6 +72,8 @@ REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", Detec
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 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 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 Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060_ULTRAW_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, 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);
@@ -461,6 +461,7 @@ REGISTER_I2C_PCI_DETECTOR("ASUS ROG MATRIX PLATINUM GeForce RTX 4090",
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5060 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5060_O8G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070_O12G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING_WHITE, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 5070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_5070TI_O16G_GAMING_OC, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5080 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5080_O16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5090_O32G_GAMING, 0x67);
@@ -468,7 +469,9 @@ REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5090 Gaming",
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_O16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 OC WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_O16G_GAMING_WHITE, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING_WHITE, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_2, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC BTF", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_BTF, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_WHITE, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL LC GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_LC_RTX_5090_O32G_GAMING, 0x67);
@@ -44,7 +44,7 @@ FnaticStreakController::~FnaticStreakController()
std::string FnaticStreakController::GetDeviceLocation()
{
return("HID " + location);
return("HID: " + location);
}
std::string FnaticStreakController::GetNameString()
@@ -230,16 +230,41 @@ void FnaticStreakController::SetLEDsDirect(std::vector<led> leds, std::vector<RG
void FnaticStreakController::SendRGBToDevice()
{
unsigned int total_leds = GetLEDCount();
unsigned int data_size = total_leds * 3;
if(keyboard_type == FNATIC_STREAK_TYPE_65)
{
unsigned char data[64];
memset(data, 0x00, sizeof(data));
/*-----------------------------------------------------*\
| For direct/software control, use command 0x0f |
| This bypasses the profile and allows immediate update |
| The 0x03 subcommand indicates per-key color data |
\*-----------------------------------------------------*/
unsigned char prefix[] = { 0x0f, 0x03 };
SendRequest(prefix, sizeof(prefix), color_buf, data_size);
data[0] = 0x0F;
data[1] = 0x15;
data[7] = 0x0F;
data[8] = 0x03;
for(unsigned int i = 9; i < 25; i++)
{
data[i] = 0xFF;
}
data[25] = color_buf[0];
data[26] = color_buf[1];
data[27] = color_buf[2];
hid_write(dev, data, sizeof(data));
}
else
{
unsigned int total_leds = GetLEDCount();
unsigned int data_size = total_leds * 3;
/*-----------------------------------------------------*\
| For direct/software control, use command 0x0f |
| This bypasses the profile and allows immediate update |
| The 0x03 subcommand indicates per-key color data |
\*-----------------------------------------------------*/
unsigned char prefix[] = { 0x0f, 0x03 };
SendRequest(prefix, sizeof(prefix), color_buf, data_size);
}
}
void FnaticStreakController::SetPulse(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed)
@@ -51,7 +51,8 @@
enum FnaticStreakType
{
FNATIC_STREAK_TYPE_FULL,
FNATIC_STREAK_TYPE_MINI
FNATIC_STREAK_TYPE_MINI,
FNATIC_STREAK_TYPE_65
};
class FnaticStreakController
@@ -26,6 +26,7 @@
#define FNATIC_STREAK_PID 0x0101
#define FNATIC_MINISTREAK_PID 0x0102
#define FNATIC_STREAK65_PID 0x0105
void DetectFnaticStreakKeyboard(hid_device_info* info, const std::string& name)
{
@@ -39,6 +40,10 @@ void DetectFnaticStreakKeyboard(hid_device_info* info, const std::string& name)
{
kb_type = FNATIC_STREAK_TYPE_MINI;
}
else if(info->product_id == FNATIC_STREAK65_PID)
{
kb_type = FNATIC_STREAK_TYPE_65;
}
else
{
kb_type = FNATIC_STREAK_TYPE_FULL;
@@ -53,3 +58,4 @@ void DetectFnaticStreakKeyboard(hid_device_info* info, const std::string& name)
REGISTER_HID_DETECTOR_I("Fnatic Streak", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_STREAK_PID, 1);
REGISTER_HID_DETECTOR_I("Fnatic miniStreak", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_MINISTREAK_PID, 1);
REGISTER_HID_DETECTOR_I("Fnatic Streak65", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_STREAK65_PID, 1);
@@ -37,19 +37,22 @@ bool TestForGalaxGPUController(i2c_smbus_interface* bus, unsigned char address)
\*-----------------------------------------------------------------*/
case 0x32:
res = bus->i2c_smbus_read_byte_data(address, 0x00);
if(res == 0x27 || res == 0x26) {
if(res == 0x27 || res == 0x26)
{
res = bus->i2c_smbus_read_byte_data(address, 0x01);
if (res == 0x10 || res == 0x20)
if(res == 0x10 || res == 0x20)
{
pass = true;
}
}
break;
/*-----------------------------------------------------------------*\
| V1 Controller - RTX 3080 |
| V1 Controller |
\*-----------------------------------------------------------------*/
case 0x23:
res = bus->i2c_smbus_read_byte_data(address, 0x00);
if(res == 0x30)
if(res == 0x27 || res == 0x30)
{
pass = true;
}
@@ -60,8 +63,10 @@ bool TestForGalaxGPUController(i2c_smbus_interface* bus, unsigned char address)
\*-----------------------------------------------------------------*/
case 0x51:
res = bus->i2c_smbus_read_byte_data(address, 0x00);
if (res == 0x80)
if(res == 0x80)
{
pass = true;
}
break;
}
@@ -199,6 +199,7 @@ REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 Gaming OC",
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 MASTER", DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_MASTER_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellAorusWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 MASTER ICE", DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_MASTER_ICE_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_GAMING_OC_32G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_XTREME_WATERFORCE_32G_SUB_DEV1, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_XTREME_WATERFORCE_32G_SUB_DEV2, 0x75);
@@ -194,6 +194,7 @@ REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7800 XT GAMING OC",
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 GRE GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900GRE_GAMING_OC_16G_SUB_DEV, 0x62);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900XT_GAMING_OC_20G_SUB_DEV, 0x62);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 XTX GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900XTX_GAMING_OC_24G_SUB_DEV, 0x62);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS Radeon RX 7900 XTX ELITE 24G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX7900XTX_ELITE_24G_SUB_DEV, 0x71);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS RX 6750 XT ELITE 12G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX_6750_XT_ELITE_12G_SUB_DEV, 0x70);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS RX 6900 XT EXTREME WATERFORCE WB", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV2, GIGABYTE_SUB_VEN, GIGABYTE_RX6900XT_XTREME_WATERFORCE_WB_SUB_DEV, 0x70);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS Radeon RX 9070 XT Elite", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX9070XT_ELITE_16G_SUB_DEV, 0x73);
File diff suppressed because it is too large Load Diff
@@ -16,7 +16,7 @@
#include <map>
#include "RGBController.h"
#define GB_FUSION2_ZONES_MAX 8
#define GB_FUSION2_ZONES_MAX 12
/*--------------------------------------------------------*\
| Base LED mappings found on all controllers. |
@@ -89,6 +89,23 @@ const FwdLedHeaders LedLookup =
\*-------------------------------------------------*/
};
/*--------------------------------------------------------*\
| The layout_id masks for supported effects. |
\*--------------------------------------------------------*/
enum GB_LID_EFFECTS_MASKS : uint32_t
{
GB_EFF_BREATH = 0x001,
GB_EFF_BEAT = 0x002,
GB_EFF_CYCLE = 0x004,
GB_EFF_FLASH = 0x008,
GB_EFF_RANDOM = 0x010,
GB_EFF_WAVE = 0x020,
GB_EFF_DFLASH = 0x040,
GB_EFF_WAVE1 = 0x080,
GB_EFF_WAVE2 = 0x100,
GB_EFF_CORE_MASK = 0x1FF
};
typedef struct
{
GB_FUSION2_LED_IDX idx;
@@ -95,10 +95,9 @@ bool RGBFusion2USBController::RefreshHardwareInfo()
return false;
}
IT8297Report report;
std::memcpy(&report, buffer, sizeof(IT8297Report));
report_loaded = true;
device_num = report.device_num;
description = std::string(report.str_product, 28);
if(std::string::iterator nul = std::find(description.begin(), description.end(), '\0');
nul != description.end())
@@ -318,7 +317,7 @@ bool RGBFusion2USBController::SetCalibration(const EncodedCalibration& cal, bool
cal_data.mainboard = desired.c.rgb_cali;
cal_data.spare[0] = desired.c.c_spare0;
cal_data.spare[1] = desired.c.c_spare1;
if(product_id == 0x5711)
{
cal_data.dled[2] = desired.c.d_strip_c2;
@@ -362,7 +361,6 @@ void RGBFusion2USBController::SetLedCount(unsigned int c0, unsigned int c1, unsi
\*---------------------------------------------------------*/
bool RGBFusion2USBController::SetStripBuiltinEffectState(int hdr, bool enable)
{
static bool first_call = true;
int bitmask = 0;
if(hdr == -1)
@@ -392,16 +390,16 @@ bool RGBFusion2USBController::SetStripBuiltinEffectState(int hdr, bool enable)
}
}
int new_effect_disabled = enable
? (effect_disabled & ~bitmask)
: (effect_disabled | bitmask);
int base_mask = (effect_disabled < 0) ? 0 : effect_disabled;
int new_effect_disabled = enable
? (base_mask & ~bitmask)
: (base_mask | bitmask);
if(!first_call && new_effect_disabled == effect_disabled)
// Skip redundant writes only after we have synchronized at least once
if(effect_disabled >= 0 && new_effect_disabled == effect_disabled)
{
return true;
}
first_call = false;
effect_disabled = new_effect_disabled;
int res = SendCCReport(0x32, effect_disabled);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
@@ -439,7 +437,7 @@ bool RGBFusion2USBController::EnableLampArray(bool enable)
{
return SendCCReport(0x48, enable ? 1 : 0);
}
std::string RGBFusion2USBController::GetDeviceName()
{
return(name);
@@ -703,3 +701,115 @@ void RGBFusion2USBController::ResetController()
}
ApplyEffect(true);
}
/*---------------------------------------------------------*\
| Check controller for gen2 ARGB support |
| Checks for supported device number |
| Then checks for supported controllers |
| Then checks for supported feature bit (SaveLEDState) |
| Finally checks for strip detection value. |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::SupportsGen2() const
{
bool supports_gen2 = false;
supports_gen2 = (device_num == 0x00)
&& (product_id == 0x5702 || product_id==0x5711 || product_id==0x8950)
&& (report.support_cmd_flag & 0x01)
&& (report.strip_detect == 0x01);
return supports_gen2;
}
std::vector<Gen2StripInfo> RGBFusion2USBController::ExportGen2Strips() const
{
size_t count = (product_id == 0x5711) ? 4u : 2u;
std::vector<Gen2StripInfo> out;
out.reserve(count);
for(size_t i = 0; i < count; ++i)
{
out.push_back(g2_strip_info[i]);
}
return out;
}
/*---------------------------------------------------------*\
| Scan Headers for Gen2 Devices |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::ScanGen2Strips()
{
for(unsigned 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);
}
const unsigned int hdr_lim = (product_id == 0x5711) ? 4u : 2u;
for(unsigned int slot = 0; slot < hdr_lim; ++slot)
{
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);
if(!SendCCReport(scan_cmd, 0x00, 0x00))
{
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(700));
if(!SendCCReport(info_cmd, 0x00, 0x00))
{
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));
if(recv_len < 64)
{
return false;
}
int seg_count = static_cast<int>(feature_buf[1]);
if(seg_count < 0)
{
seg_count = 0;
}
if(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;
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;
}
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;
}
@@ -21,6 +21,11 @@
#define FUSION2_USB_BUFFER_SIZE 64
/*--------------------------------------------------------*\
| Gen2 scan/info opcode base |
\*--------------------------------------------------------*/
#define GEN2_LED_BASE_SCAN 0x38
/*---------------------------------------------------------*\
| Effects mode list |
\*---------------------------------------------------------*/
@@ -83,6 +88,16 @@ struct EncodedCalibration
std::string mainboard;
};
/*---------------------------------------------------------*\
| High level struct to contain Gen2 Header Data |
\*---------------------------------------------------------*/
struct Gen2StripInfo
{
uint8_t numStrip = 0;
std::vector<uint16_t> LedsOfStrip;
uint32_t totalLeds = 0;
};
#pragma pack(push, 1)
/*---------------------------------------------------------*\
@@ -321,6 +336,8 @@ public:
void SetLEDEffect(int led, int mode, unsigned int speed, unsigned char brightness, bool random, uint32_t* color);
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();
EncodedCalibration GetCalibration(bool refresh_from_hw = false);
std::string GetDeviceName();
@@ -330,6 +347,8 @@ public:
std::string GetFWVersion();
uint16_t GetProductID();
std::string GetSerial();
std::vector<Gen2StripInfo> ExportGen2Strips() const;
Gen2StripInfo g2_strip_info[4];
private:
std::string DecodeCalibrationBuffer(uint32_t value) const;
@@ -345,7 +364,7 @@ private:
int SendPacket(unsigned char* packet);
hid_device* dev;
int device_num;
uint8_t device_num;
uint16_t product_id;
uint32_t effect_zone_mask = 0;
int mode;
@@ -356,7 +375,7 @@ private:
std::string location;
std::string version;
std::string chip_id;
int effect_disabled = 0;
int effect_disabled = -1;
int report_id = 0xCC;
bool report_loaded = false;
bool cali_loaded = false;
@@ -25,7 +25,7 @@
@effects :white_check_mark:
@detectors DetectGigabyteRGBFusion2USBControllers
@comment The Fusion 2 USB controller applies to most AMD and
Intel mainboards from the x570 and z390 chipsets onwards.
Intel mainboards from the X570 and z390 chipsets onwards.
\*-------------------------------------------------------------------*/
RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController* controller_ptr, std::string detector)
@@ -39,8 +39,10 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
version = controller->GetFWVersion();
location = controller->GetDeviceLocation();
serial = controller->GetSerial();
product_id = controller->GetProductID();
device_num = controller->GetDeviceNum();
mode Direct;
Direct.name = "Direct";
Direct.value = 0xFFFF;
@@ -206,6 +208,13 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
RGBController_RGBFusion2USB::~RGBController_RGBFusion2USB()
{
// Free any zones we allocated for the per-instance layout
for(gb_fusion2_zone* z : allocated_zones)
{
delete z;
}
allocated_zones.clear();
delete controller;
}
@@ -214,94 +223,125 @@ RGBController_RGBFusion2USB::~RGBController_RGBFusion2USB()
\*---------------------------------------------------------*/
void RGBController_RGBFusion2USB::Init_Controller()
{
const std::string SectionCustom = "CustomLayout";
const gb_fusion2_device* src_layout = gb_fusion2_device_list[device_index];
const std::string SectionGen2 = "Gigabyte-Gen2-ARGB";
const std::string SectionCustomBase = "CustomLayout";
const std::string SectionCustom = SectionCustomBase + std::to_string(device_num);
const std::string SectionCalibration = "Calibration";
RvrseLedHeaders ReverseLedLookup = reverse_map(LedLookup);
SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager();
nlohmann::json device_settings = settings_manager->GetSettings(detector_name);
/*---------------------------------------------------------*\
| Create the custom layout from the generic_device |
| Checks for Gen2 support and adds flag to json. |
\*---------------------------------------------------------*/
gb_fusion2_device* layout = const_cast<gb_fusion2_device*>(gb_fusion2_device_list[device_index]);
if(controller->SupportsGen2())
{
if(!device_settings.contains(SectionGen2))
{
device_settings[SectionGen2]["Enabled"] = false;
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
supports_gen2 = device_settings[SectionGen2]["Enabled"];
if(supports_gen2)
{
controller->ScanGen2Strips();
}
}
/*---------------------------------------------------------*\
| Create the custom layout from the generic layout |
\*---------------------------------------------------------*/
switch(product_id)
{
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;
}
if(!device_settings.contains(SectionCustom))
{
device_settings[SectionCustom]["Enabled"] = false;
device_settings[SectionCustom]["Data"] = BuildCustomLayoutJson(layout, ReverseLedLookup);
device_settings[SectionCustom]["Data"] = BuildCustomLayoutJson(src_layout, ReverseLedLookup);
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
bool custom_layout = device_settings[SectionCustom]["Enabled"];
if(custom_layout)
{
LoadCustomLayoutFromJson(device_settings[SectionCustom]["Data"], LedLookup, layout);
}
EncodedCalibration hw_cal = controller->GetCalibration(false);
if(!device_settings.contains(SectionCalibration))
if(device_num == 0)
{
device_settings[SectionCalibration]["Enabled"] = false;
device_settings[SectionCalibration]["Data"] = WriteCalJsonFrom(hw_cal);
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
else
{
nlohmann::json& cal_sec = device_settings[SectionCalibration];
bool cal_enable = cal_sec.value("Enabled", false);
if(!cal_sec.contains("Data") || !cal_sec["Data"].is_object())
if(!device_settings.contains(SectionCalibration))
{
cal_sec["Data"] = WriteCalJsonFrom(hw_cal);
device_settings[SectionCalibration]["Enabled"] = false;
device_settings[SectionCalibration]["Data"] = WriteCalJsonFrom(hw_cal);
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
else
{
nlohmann::json& cdata = cal_sec["Data"];
FillMissingWith(cdata, hw_cal);
nlohmann::json& cal_sec = device_settings[SectionCalibration];
bool cal_enable = cal_sec.value("Enabled", false);
if(!cal_enable)
if(!cal_sec.contains("Data") || !cal_sec["Data"].is_object())
{
cal_sec["Data"] = WriteCalJsonFrom(hw_cal);
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
}
if(cal_enable)
{
const nlohmann::json& cdata = cal_sec["Data"];
EncodedCalibration desired;
desired.dled[0] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED1");
desired.dled[1] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED2");
desired.mainboard = GET_JSON_VAL_ELSE_OFF(cdata, "Mainboard");
desired.spare[0] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare0");
desired.spare[1] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare1");
if(controller->GetProductID() == 0x5711)
{
desired.dled[2] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED3");
desired.dled[3] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED4");
desired.spare[2] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare2");
desired.spare[3] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare3");
}
else
{
desired.dled[2] = "OFF";
desired.dled[3] = "OFF";
desired.spare[2] = "OFF";
desired.spare[3] = "OFF";
nlohmann::json& cdata = cal_sec["Data"];
FillMissingWith(cdata, hw_cal);
if(!cal_enable)
{
cal_sec["Data"] = WriteCalJsonFrom(hw_cal);
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
}
if(cal_enable)
{
const nlohmann::json& cdata = cal_sec["Data"];
EncodedCalibration desired;
desired.dled[0] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED1");
desired.dled[1] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED2");
desired.mainboard = GET_JSON_VAL_ELSE_OFF(cdata, "Mainboard");
desired.spare[0] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare0");
desired.spare[1] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare1");
if(controller->GetProductID() == 0x5711)
{
desired.dled[2] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED3");
desired.dled[3] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED4");
desired.spare[2] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare2");
desired.spare[3] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare3");
}
else
{
desired.dled[2] = "OFF";
desired.dled[3] = "OFF";
desired.spare[2] = "OFF";
desired.spare[3] = "OFF";
}
controller->SetCalibration(desired, false);
}
controller->SetCalibration(desired, false);
}
}
/*---------------------------------------------------------------------*\
| When no match found the first entry (generic_device) will be used |
| otherwise look up channel map based on device name |
@@ -315,35 +355,81 @@ void RGBController_RGBFusion2USB::Init_Controller()
\*-----------------------------------------------------------------*/
for(unsigned int i = 0; i < GB_FUSION2_DEVICE_COUNT; i++)
{
if(gb_fusion2_device_list[i]->name == name)
if(gb_fusion2_device_list[i]->name == name &&
gb_fusion2_device_list[i]->device_num == device_num)
{
/*---------------------------------------------------------*\
| Set device ID |
\*---------------------------------------------------------*/
device_index = i;
layout = const_cast<gb_fusion2_device*>(gb_fusion2_device_list[i]);
src_layout = gb_fusion2_device_list[i];
break;
}
}
}
/*---------------------------------------------------------------------*\
| Creates per instance copy of layouts. |
\*---------------------------------------------------------------------*/
instance_layout.zones = &instance_zones;
instance_layout.layout_id = src_layout->layout_id;
instance_layout.device_num = src_layout->device_num;
instance_layout.name = src_layout->name;
for(uint8_t zi = 0; zi < GB_FUSION2_ZONES_MAX; ++zi)
{
(*instance_layout.zones)[zi] = (*src_layout->zones)[zi];
}
if(custom_layout)
{
LoadCustomLayoutFromJson(device_settings[SectionCustom]["Data"], LedLookup, &instance_layout);
}
/*---------------------------------------------------------------------*\
| Culls the mode support based on layout_id. |
\*---------------------------------------------------------------------*/
const uint32_t effect_mask = instance_layout.layout_id & GB_EFF_CORE_MASK;
modes.erase(std::remove_if(modes.begin(), modes.end(),
[effect_mask](const mode& m)
{
if(m.value == 0xFFFF /* Direct */) { return false; }
if(m.value == EFFECT_STATIC) { return false; }
uint32_t bit = 0u;
switch(m.value)
{
case EFFECT_PULSE: bit = GB_EFF_BREATH; break;
case EFFECT_COLORCYCLE: bit = GB_EFF_CYCLE; break;
case EFFECT_BLINKING: bit = GB_EFF_FLASH; break;
case EFFECT_RANDOM: bit = GB_EFF_RANDOM; break;
case EFFECT_WAVE: bit = GB_EFF_WAVE; break;
case EFFECT_DFLASH: bit = GB_EFF_DFLASH; break;
case EFFECT_WAVE1: bit = GB_EFF_WAVE1; break;
case EFFECT_WAVE2: bit = GB_EFF_WAVE2; break;
case EFFECT_WAVE3: bit = GB_EFF_WAVE1; break;
case EFFECT_WAVE4: bit = GB_EFF_WAVE2; break;
default: bit = 0u; break;
}
return (bit == 0u) || ((effect_mask & bit) == 0u);
}),
modes.end());
/*---------------------------------------------------------*\
| Iterate through layout and process each zone |
\*---------------------------------------------------------*/
for(uint8_t zone_idx = 0; zone_idx < GB_FUSION2_ZONES_MAX; zone_idx++)
{
if(!layout->zones[0][zone_idx])
if(!(*instance_layout.zones)[zone_idx])
{
continue;
}
const gb_fusion2_zone* zone_at_idx = layout->zones[0][zone_idx];
const gb_fusion2_zone* zone_at_idx = (*instance_layout.zones)[zone_idx];
zone new_zone;
new_zone.name = zone_at_idx->name;
new_zone.leds_min = zone_at_idx->leds_min;
new_zone.leds_max = zone_at_idx->leds_max;
new_zone.leds_count = new_zone.leds_min;
new_zone.type = (new_zone.leds_min == new_zone.leds_max) ? ZONE_TYPE_SINGLE : ZONE_TYPE_LINEAR;
new_zone.type = ((new_zone.leds_min == 1) && (new_zone.leds_max == 1)) ? ZONE_TYPE_SINGLE : ZONE_TYPE_LINEAR;
new_zone.matrix_map = NULL;
zones.emplace_back(new_zone);
}
@@ -363,17 +449,68 @@ void RGBController_RGBFusion2USB::SetupZones()
| Set up zones (Fixed so as to not spam the controller) |
\*---------------------------------------------------------*/
std::vector<Gen2StripInfo> strips = controller->ExportGen2Strips();
for(uint8_t zone_idx = 0; zone_idx < GB_FUSION2_ZONES_MAX; zone_idx++)
{
const gb_fusion2_zone* zone_at_idx = gb_fusion2_device_list[device_index]->zones[0][zone_idx];
const gb_fusion2_zone* zone_at_idx = (*instance_layout.zones)[zone_idx];
if(!zone_at_idx)
{
continue;
}
bool single_zone = (zone_at_idx->leds_min == zone_at_idx->leds_max);
bool single_zone = ((zone_at_idx->leds_min == 1) && (zone_at_idx->leds_max == 1));
if(!single_zone)
{
if(supports_gen2)
{
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:
break;
}
if(slot >= 0 && static_cast<size_t>(slot) < strips.size())
{
const Gen2StripInfo& info = strips[static_cast<size_t>(slot)];
if(info.totalLeds > 0u)
{
zones[zone_idx].leds_count = static_cast<unsigned int>(info.totalLeds);
zones[zone_idx].leds_min = static_cast<unsigned int>(info.totalLeds);
zones[zone_idx].leds_max = static_cast<unsigned int>(info.totalLeds);
zones[zone_idx].segments.clear();
zones[zone_idx].segments.reserve(info.LedsOfStrip.size());
unsigned int offset = 0;
for(size_t si = 0; si < info.LedsOfStrip.size(); ++si)
{
const uint16_t cnt = info.LedsOfStrip[si];
if(cnt == 0) continue;
segment seg;
seg.name = std::string("Segment ") + std::to_string(si);
seg.type = ZONE_TYPE_LINEAR;
seg.start_idx = offset;
seg.leds_count = static_cast<unsigned int>(cnt);
zones[zone_idx].segments.push_back(seg);
offset += static_cast<unsigned int>(cnt);
}
}
}
}
switch(zone_at_idx->idx)
{
case LED4:
@@ -826,6 +963,7 @@ void RGBController_RGBFusion2USB::LoadCustomLayoutFromJson(
&& new_zone->idx <= GB_FUSION2_LED_IDX::LED11)
{
layout->zones[0][zone_idx] = new_zone;
allocated_zones.push_back(new_zone);
}
else
{
@@ -62,17 +62,26 @@ private:
std::string detector_name;
RGBFusion2USBController* controller;
int device_num;
uint8_t device_num;
RGBColor null_color = 0;
bool supports_gen2 = 0;
/*---------------------------------------------------------*\
| The intial value of device_index should point to the |
| layout for the generic_device |
\*---------------------------------------------------------*/
uint32_t device_index = 0;
uint16_t product_id = 0;
uint32_t effects_mask = 0;
void Init_Controller();
int GetLED_Zone(int led_idx);
/*---------------------------------------------------------*\
| Per instance layout lookup tables. |
\*---------------------------------------------------------*/
gb_fusion2_device instance_layout{};
gb_fusion2_layout instance_zones{};
std::vector<gb_fusion2_zone*> allocated_zones;
nlohmann::json WriteCalJsonFrom(
const EncodedCalibration& src);
void FillMissingWith(
@@ -20,6 +20,8 @@ static std::map<std::string, unsigned int> govee_led_counts
{
{ "H619A", 20 },
{ "H70B1", 20 },
{ "H612F", 12 },
{ "H607C", 174 },
};
RGBController_Govee::RGBController_Govee(GoveeController* controller_ptr)
@@ -0,0 +1,121 @@
/*---------------------------------------------------------*\
| HyperXEve1800Controller.cpp |
| |
| Driver for HyperX Eve 1800 keyboard |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include "HyperXEve1800Controller.h"
#include "StringUtils.h"
HyperXEve1800Controller::HyperXEve1800Controller(hid_device* dev_handle, const char* path, std::string dev_name)
{
dev = dev_handle;
location = path;
name = dev_name;
}
HyperXEve1800Controller::~HyperXEve1800Controller()
{
hid_close(dev);
}
std::string HyperXEve1800Controller::GetDeviceLocation()
{
return("HID: " + location);
}
std::string HyperXEve1800Controller::GetNameString()
{
return(name);
}
std::string HyperXEve1800Controller::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 HyperXEve1800Controller::SetBrightness(unsigned int brightness)
{
unsigned char buf[65];
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x40;
buf[0x01] = 0x01;
buf[0x02] = 0x00;
buf[0x03] = 0x00;
buf[0x04] = brightness & 0xFF;
hid_write(dev, buf, 65);
}
void HyperXEve1800Controller::SetLEDsDirect(std::vector<RGBColor> colors)
{
SendDirectInitialization();
if(colors.empty())
{
RGBColor temp = 0x00000000;
SendDirectColorPacket(&temp, 1);
}
else
{
SendDirectColorPacket(&colors[0], (unsigned int)colors.size());
}
}
void HyperXEve1800Controller::SendDirectInitialization()
{
unsigned char buf[65];
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x44;
buf[0x01] = 0x01;
buf[0x02] = 0x04;
buf[0x03] = 0x00;
hid_write(dev, buf, 65);
}
void HyperXEve1800Controller::SendDirectColorPacket(RGBColor* color_data, unsigned int color_count)
{
unsigned char buf[65];
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x44;
buf[0x01] = 0x02;
buf[0x02] = 0x00;
buf[0x03] = 0x00;
/*-----------------------------------------------------*\
| The Eve 1800 exposes 10 lighting zones, but this |
| report format can carry up to 20 RGB triplets. |
\*-----------------------------------------------------*/
if(color_count > 20)
{
color_count = 20;
}
for(unsigned int color_idx = 0; color_idx < color_count; color_idx++)
{
buf[4 + (color_idx * 3)] = RGBGetRValue(color_data[color_idx]);
buf[4 + (color_idx * 3) + 1] = RGBGetGValue(color_data[color_idx]);
buf[4 + (color_idx * 3) + 2] = RGBGetBValue(color_data[color_idx]);
}
hid_write(dev, buf, 65);
}
@@ -0,0 +1,36 @@
/*---------------------------------------------------------*\
| HyperXEve1800Controller.h |
| |
| Driver for HyperX Eve 1800 keyboard |
| |
| 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"
class HyperXEve1800Controller
{
public:
HyperXEve1800Controller(hid_device* dev_handle, const char* path, std::string dev_name);
~HyperXEve1800Controller();
std::string GetDeviceLocation();
std::string GetNameString();
std::string GetSerialString();
void SetBrightness(unsigned int brightness);
void SetLEDsDirect(std::vector<RGBColor> colors);
private:
hid_device* dev;
std::string location;
std::string name;
void SendDirectInitialization();
void SendDirectColorPacket(RGBColor* color_data, unsigned int color_count);
};
@@ -0,0 +1,127 @@
/*---------------------------------------------------------*\
| RGBController_HyperXEve1800.cpp |
| |
| RGBController for HyperX Eve 1800 keyboard |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_HyperXEve1800.h"
using namespace std::chrono_literals;
#define HYPERX_EVE_1800_BRIGHTNESS_MIN 0x00
#define HYPERX_EVE_1800_BRIGHTNESS_MAX 0xFF
#define HYPERX_EVE_1800_ZONE_COUNT 10
/**------------------------------------------------------------------*\
@name HyperX Eve 1800
@category Keyboard
@type USB
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectHyperXEve1800
@comment
\*-------------------------------------------------------------------*/
RGBController_HyperXEve1800::RGBController_HyperXEve1800(HyperXEve1800Controller* controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "HyperX";
type = DEVICE_TYPE_KEYBOARD;
description = "HyperX Eve 1800 Keyboard Device";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
mode Direct;
Direct.name = "Direct";
Direct.value = 0xFFFF;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.brightness_min = HYPERX_EVE_1800_BRIGHTNESS_MIN;
Direct.brightness_max = HYPERX_EVE_1800_BRIGHTNESS_MAX;
Direct.brightness = HYPERX_EVE_1800_BRIGHTNESS_MAX;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
keepalive_thread_run = 1;
keepalive_thread = new std::thread(&RGBController_HyperXEve1800::KeepaliveThread, this);
}
RGBController_HyperXEve1800::~RGBController_HyperXEve1800()
{
keepalive_thread_run = 0;
keepalive_thread->join();
delete keepalive_thread;
delete controller;
}
void RGBController_HyperXEve1800::SetupZones()
{
zone new_zone;
new_zone.name = "Keyboard";
new_zone.type = ZONE_TYPE_LINEAR;
new_zone.leds_min = HYPERX_EVE_1800_ZONE_COUNT;
new_zone.leds_max = HYPERX_EVE_1800_ZONE_COUNT;
new_zone.leds_count = HYPERX_EVE_1800_ZONE_COUNT;
new_zone.matrix_map = NULL;
zones.push_back(new_zone);
for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++)
{
led new_led;
new_led.name = "Zone ";
new_led.name.append(std::to_string(led_idx + 1));
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_HyperXEve1800::ResizeZone(int /*zone*/, int /*new_size*/)
{
}
void RGBController_HyperXEve1800::DeviceUpdateLEDs()
{
controller->SetLEDsDirect(colors);
last_update_time = std::chrono::steady_clock::now();
}
void RGBController_HyperXEve1800::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_HyperXEve1800::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_HyperXEve1800::DeviceUpdateMode()
{
controller->SetBrightness(modes[active_mode].brightness);
DeviceUpdateLEDs();
}
void RGBController_HyperXEve1800::KeepaliveThread()
{
while(keepalive_thread_run.load())
{
if(active_mode == 0)
{
if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50))
{
UpdateLEDs();
}
}
std::this_thread::sleep_for(10ms);
}
}
@@ -0,0 +1,39 @@
/*---------------------------------------------------------*\
| RGBController_HyperXEve1800.h |
| |
| RGBController for HyperX Eve 1800 keyboard |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <chrono>
#include "RGBController.h"
#include "HyperXEve1800Controller.h"
class RGBController_HyperXEve1800 : public RGBController
{
public:
RGBController_HyperXEve1800(HyperXEve1800Controller* controller_ptr);
~RGBController_HyperXEve1800();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void KeepaliveThread();
private:
HyperXEve1800Controller* controller;
std::thread* keepalive_thread;
std::atomic<bool> keepalive_thread_run;
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
};
@@ -15,6 +15,7 @@
#include "HyperXAlloyOriginsController.h"
#include "HyperXAlloyOriginsCoreController.h"
#include "HyperXAlloyOrigins60and65Controller.h"
#include "HyperXEve1800Controller.h"
#include "HyperXOrigins2_65Controller.h"
#include "RGBController_HyperXAlloyElite.h"
#include "RGBController_HyperXAlloyElite2.h"
@@ -22,6 +23,7 @@
#include "RGBController_HyperXAlloyOrigins.h"
#include "RGBController_HyperXAlloyOriginsCore.h"
#include "RGBController_HyperXAlloyOrigins60and65.h"
#include "RGBController_HyperXEve1800.h"
#include "RGBController_HyperXOrigins2_65.h"
/*-----------------------------------------------------*\
@@ -46,6 +48,7 @@
#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)
@@ -156,6 +159,19 @@ void DetectHyperXOrigins2_65(hid_device_info* info, const std::string& name)
}
}
void DetectHyperXEve1800(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
HyperXEve1800Controller* controller = new HyperXEve1800Controller(dev, info->path, name);
RGBController_HyperXEve1800* rgb_controller = new RGBController_HyperXEve1800(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
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);
@@ -163,6 +179,7 @@ REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core", DetectHyperXAlloyOrigi
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);
@@ -0,0 +1,95 @@
/*---------------------------------------------------------*\
| MSILaptopController.cpp |
| |
| Driver for MSI laptop SteelSeries KLC/ALC RGB devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include "MSILaptopController.h"
#include "StringUtils.h"
#define MSI_LAPTOP_REPORT_ID 0x00
#define MSI_LAPTOP_COMMAND 0x0C
#define MSI_LAPTOP_KLC_PACKET_ID 0x66
#define MSI_LAPTOP_ALC_PACKET_ID 0x06
#define MSI_LAPTOP_PACKET_SIZE 525
#define MSI_LAPTOP_PAYLOAD_OFFSET 5
MSILaptopController::MSILaptopController(hid_device* dev_handle, const char* path, std::string dev_name, msi_laptop_device device_type)
{
dev = dev_handle;
location = path;
name = dev_name;
type = device_type;
}
MSILaptopController::~MSILaptopController()
{
hid_close(dev);
}
std::string MSILaptopController::GetDeviceLocation()
{
return("HID: " + location);
}
std::string MSILaptopController::GetDeviceName()
{
return(name);
}
std::string MSILaptopController::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));
}
msi_laptop_device MSILaptopController::GetDeviceType()
{
return(type);
}
void MSILaptopController::SetLEDs(std::vector<led> leds, std::vector<RGBColor> colors)
{
unsigned char buf[MSI_LAPTOP_PACKET_SIZE];
unsigned int led_count = (leds.size() < colors.size()) ? leds.size() : colors.size();
memset(buf, 0x00, sizeof(buf));
buf[0x00] = MSI_LAPTOP_REPORT_ID;
buf[0x01] = MSI_LAPTOP_COMMAND;
buf[0x03] = (type == MSI_LAPTOP_KLC) ? MSI_LAPTOP_KLC_PACKET_ID : MSI_LAPTOP_ALC_PACKET_ID;
// Fill unused LED IDs with 0xFF so they are ignored by the controller
for(int i = 0; i < (MSI_LAPTOP_PACKET_SIZE - MSI_LAPTOP_PAYLOAD_OFFSET) / 4; i++)
{
buf[MSI_LAPTOP_PAYLOAD_OFFSET + (i * 4)] = 0xFF;
}
for(unsigned int led_idx = 0; led_idx < led_count; led_idx++)
{
unsigned int offset = MSI_LAPTOP_PAYLOAD_OFFSET + (led_idx * 4);
if((offset + 3) >= sizeof(buf))
{
break;
}
buf[offset + 0] = leds[led_idx].value;
buf[offset + 1] = RGBGetRValue(colors[led_idx]);
buf[offset + 2] = RGBGetGValue(colors[led_idx]);
buf[offset + 3] = RGBGetBValue(colors[led_idx]);
}
hid_send_feature_report(dev, buf, sizeof(buf));
}
@@ -0,0 +1,67 @@
/*---------------------------------------------------------*\
| MSILaptopController.h |
| |
| Driver for MSI laptop SteelSeries KLC/ALC RGB devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <vector>
#include <hidapi.h>
#include "RGBController.h"
typedef enum
{
MSI_LAPTOP_KLC,
MSI_LAPTOP_ALC,
} msi_laptop_device;
typedef struct
{
const char* name;
unsigned char id;
} msi_laptop_led;
struct MSILaptopModel
{
const char* sys_vendor;
const char* product_name;
/* Keyboard layout */
const msi_laptop_led* klc_leds;
unsigned int klc_leds_count;
unsigned int klc_matrix_height;
unsigned int klc_matrix_width;
const unsigned int* klc_matrix_map;
/* Lightbar layout */
const msi_laptop_led* alc_leds;
unsigned int alc_leds_count;
unsigned int alc_lightbar_leds;
};
class MSILaptopController
{
public:
MSILaptopController(hid_device* dev_handle, const char* path, std::string dev_name, msi_laptop_device device_type);
~MSILaptopController();
std::string GetDeviceLocation();
std::string GetDeviceName();
std::string GetSerialString();
msi_laptop_device GetDeviceType();
void SetLEDs(std::vector<led> leds, std::vector<RGBColor> colors);
private:
hid_device* dev;
std::string location;
std::string name;
msi_laptop_device type;
};
@@ -0,0 +1,235 @@
/*---------------------------------------------------------*\
| MSILaptopControllerDetect.cpp |
| |
| Detector for MSI laptop SteelSeries RGB devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "RGBControllerKeyNames.h"
#include "RGBController_MSILaptop.h"
#include "MSILaptopController.h"
#include "dmiinfo.h"
#define MSI_LAPTOP_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define STEELSERIES_VID 0x1038
#define STEELSERIES_MSI_RAIDER_A18_KLC_PID 0x1122
#define STEELSERIES_MSI_RAIDER_A18_ALC_PID 0x1161
#define NA 0xFFFFFFFF
#define MSI_LAPTOP_KLC_MATRIX_HEIGHT 6
#define MSI_LAPTOP_KLC_MATRIX_WIDTH 23
#define MSI_LAPTOP_ALC_LIGHTBAR_LEDS 3
static const msi_laptop_led msi_raider_a18_klc_leds[] =
{
{ KEY_EN_A, 0x04 },
{ KEY_EN_B, 0x05 },
{ KEY_EN_C, 0x06 },
{ KEY_EN_D, 0x07 },
{ KEY_EN_E, 0x08 },
{ KEY_EN_F, 0x09 },
{ KEY_EN_G, 0x0A },
{ KEY_EN_H, 0x0B },
{ KEY_EN_I, 0x0C },
{ KEY_EN_J, 0x0D },
{ KEY_EN_K, 0x0E },
{ KEY_EN_L, 0x0F },
{ KEY_EN_M, 0x10 },
{ KEY_EN_N, 0x11 },
{ KEY_EN_O, 0x12 },
{ KEY_EN_P, 0x13 },
{ KEY_EN_Q, 0x14 },
{ KEY_EN_R, 0x15 },
{ KEY_EN_S, 0x16 },
{ KEY_EN_T, 0x17 },
{ KEY_EN_U, 0x18 },
{ KEY_EN_V, 0x19 },
{ KEY_EN_W, 0x1A },
{ KEY_EN_X, 0x1B },
{ KEY_EN_Y, 0x1C },
{ KEY_EN_Z, 0x1D },
{ KEY_EN_1, 0x1E },
{ KEY_EN_2, 0x1F },
{ KEY_EN_3, 0x20 },
{ KEY_EN_4, 0x21 },
{ KEY_EN_5, 0x22 },
{ KEY_EN_6, 0x23 },
{ KEY_EN_7, 0x24 },
{ KEY_EN_8, 0x25 },
{ KEY_EN_9, 0x26 },
{ KEY_EN_0, 0x27 },
{ KEY_EN_ESCAPE, 0x29 },
{ KEY_EN_TAB, 0x2B },
{ KEY_EN_SPACE, 0x2C },
{ KEY_EN_MINUS, 0x2D },
{ KEY_EN_EQUALS, 0x2E },
{ KEY_EN_LEFT_BRACKET, 0x2F },
{ KEY_EN_RIGHT_BRACKET, 0x30 },
{ KEY_EN_SEMICOLON, 0x33 },
{ KEY_EN_QUOTE, 0x34 },
{ KEY_EN_BACK_TICK, 0x35 },
{ KEY_EN_COMMA, 0x36 },
{ KEY_EN_PERIOD, 0x37 },
{ KEY_EN_FORWARD_SLASH, 0x38 },
{ KEY_EN_CAPS_LOCK, 0x39 },
{ KEY_EN_F1, 0x3A },
{ KEY_EN_F2, 0x3B },
{ KEY_EN_F3, 0x3C },
{ KEY_EN_F4, 0x3D },
{ KEY_EN_F5, 0x3E },
{ KEY_EN_F6, 0x3F },
{ KEY_EN_F7, 0x40 },
{ KEY_EN_F8, 0x41 },
{ KEY_EN_F9, 0x42 },
{ KEY_EN_F10, 0x43 },
{ KEY_EN_F11, 0x44 },
{ KEY_EN_F12, 0x45 },
{ KEY_EN_PRINT_SCREEN, 0x46 },
{ KEY_EN_SCROLL_LOCK, 0x47 },
{ KEY_EN_INSERT, 0x49 },
{ "Home/Page Up", 0x4B },
{ KEY_EN_DELETE, 0x4C },
{ KEY_EN_PAGE_DOWN, 0x4E },
{ KEY_EN_RIGHT_ARROW, 0x4F },
{ KEY_EN_LEFT_ARROW, 0x50 },
{ KEY_EN_DOWN_ARROW, 0x51 },
{ KEY_EN_UP_ARROW, 0x52 },
{ KEY_EN_NUMPAD_LOCK, 0x53 },
{ KEY_EN_NUMPAD_DIVIDE, 0x54 },
{ KEY_EN_NUMPAD_TIMES, 0x55 },
{ KEY_EN_NUMPAD_MINUS, 0x56 },
{ KEY_EN_NUMPAD_PLUS, 0x57 },
{ KEY_EN_NUMPAD_ENTER, 0x58 },
{ KEY_EN_NUMPAD_1, 0x59 },
{ KEY_EN_NUMPAD_2, 0x5A },
{ KEY_EN_NUMPAD_3, 0x5B },
{ KEY_EN_NUMPAD_4, 0x5C },
{ KEY_EN_NUMPAD_5, 0x5D },
{ KEY_EN_NUMPAD_6, 0x5E },
{ KEY_EN_NUMPAD_7, 0x5F },
{ KEY_EN_NUMPAD_8, 0x60 },
{ KEY_EN_NUMPAD_9, 0x61 },
{ KEY_EN_NUMPAD_0, 0x62 },
{ KEY_EN_NUMPAD_PERIOD, 0x63 },
{ KEY_EN_POWER, 0x66 },
{ KEY_EN_LEFT_CONTROL, 0xE0 },
{ KEY_EN_LEFT_SHIFT, 0xE1 },
{ KEY_EN_LEFT_ALT, 0xE2 },
{ KEY_EN_LEFT_WINDOWS, 0xE3 },
{ KEY_EN_RIGHT_WINDOWS, 0xE4 },
{ KEY_EN_RIGHT_FUNCTION, 0xF0 },
{ KEY_EN_ANSI_ENTER, 0x28 },
{ KEY_EN_BACKSPACE, 0x2A },
{ KEY_EN_BACK_SLASH, 0x31 },
{ KEY_EN_ISO_BACK_SLASH, 0x64 },
{ KEY_EN_RIGHT_SHIFT, 0xE5 },
{ KEY_EN_RIGHT_ALT, 0xE6 },
};
static unsigned int msi_raider_a18_klc_matrix_map[MSI_LAPTOP_KLC_MATRIX_HEIGHT][MSI_LAPTOP_KLC_MATRIX_WIDTH] =
{
{ 36, NA, 50, 51, 52, 53, NA, 54, 55, 56, 57, NA, 58, 59, 60, 61, 62, 63, NA, NA, NA, NA, 89 },
{ 45, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 39, 40, 97, NA, 64, 65, NA, 72, 73, 74, 75, NA },
{ 37, 16, 22, 4, 17, 19, 24, 20, 8, 14, 15, 41, 42, 98, NA, 66, 67, NA, 84, 85, 86, 76, NA },
{ 49, 0, 18, 3, 5, 6, 7, 9, 10, 11, 43, 44, 96, NA, NA, NA, NA, NA, 81, 82, 83, NA, NA },
{ 91, 25, 23, 2, 21, 1, 13, 12, 46, 47, 48,100, NA, NA, NA, NA, 71, NA, 78, 79, 80, 77, NA },
{ 90, 93, 92, NA, NA, NA, 38, NA, NA, NA,101, 94, 95, NA, 69, 70, 68, NA, 87, NA, 88, NA, NA },
};
/*---------------------------------------------------------*\
| Note on Raider A18 HX ALC (Lightbar) LED mappings: |
| Initial reverse engineering packet captures showed 6 zones|
| being updated (indexes 0x00 to 0x05). |
| However, indexes 0x04 (L4) and 0x05 (L5) are "dummy" zones|
| on this specific model and do not correspond to physical |
| LEDs. To avoid user confusion, they are omitted from the |
| UI profile here. |
| The SetLEDs function handles zero-padding the unused |
| payload slots with 0xFF so that omitting these zones |
| doesn't accidentally overwrite the 0x00 index (L1). |
\*---------------------------------------------------------*/
static const msi_laptop_led msi_raider_a18_alc_leds[] =
{
{ "Lightbar 1", 0x00 },
{ "Lightbar 2", 0x01 },
{ "Lightbar 3", 0x02 },
{ "Logo", 0x03 },
};
static const MSILaptopModel msi_laptop_models[] =
{
{
"Micro-Star International Co., Ltd.",
"Raider A18 HX A9WJG",
/* Keyboard layout */
msi_raider_a18_klc_leds,
MSI_LAPTOP_ARRAY_SIZE(msi_raider_a18_klc_leds),
MSI_LAPTOP_KLC_MATRIX_HEIGHT,
MSI_LAPTOP_KLC_MATRIX_WIDTH,
(const unsigned int*)msi_raider_a18_klc_matrix_map,
/* Lightbar layout */
msi_raider_a18_alc_leds,
MSI_LAPTOP_ARRAY_SIZE(msi_raider_a18_alc_leds),
MSI_LAPTOP_ALC_LIGHTBAR_LEDS,
},
};
static const MSILaptopModel* GetMSILaptopModelDMI()
{
DMIInfo dmi;
for(unsigned int i = 0; i < MSI_LAPTOP_ARRAY_SIZE(msi_laptop_models); i++)
{
if((dmi.getManufacturer() == msi_laptop_models[i].sys_vendor) &&
(dmi.getProductName() == msi_laptop_models[i].product_name))
{
return &msi_laptop_models[i];
}
}
return nullptr;
}
void DetectMSILaptop(hid_device_info* info, const std::string& name)
{
const MSILaptopModel* model = GetMSILaptopModelDMI();
if(!model)
{
return;
}
msi_laptop_device device_type;
if(info->product_id == STEELSERIES_MSI_RAIDER_A18_KLC_PID)
{
device_type = MSI_LAPTOP_KLC;
}
else if(info->product_id == STEELSERIES_MSI_RAIDER_A18_ALC_PID)
{
device_type = MSI_LAPTOP_ALC;
}
else
{
return;
}
hid_device* dev = hid_open_path(info->path);
if(dev)
{
MSILaptopController* controller = new MSILaptopController(dev, info->path, name, device_type);
RGBController_MSILaptop* rgb_controller = new RGBController_MSILaptop(controller, model);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_HID_DETECTOR("MSI Laptop Keyboard", DetectMSILaptop, STEELSERIES_VID, STEELSERIES_MSI_RAIDER_A18_KLC_PID);
REGISTER_HID_DETECTOR_I("MSI Laptop Lightbar", DetectMSILaptop, STEELSERIES_VID, STEELSERIES_MSI_RAIDER_A18_ALC_PID, 0);
@@ -0,0 +1,141 @@
/*---------------------------------------------------------*\
| RGBController_MSILaptop.cpp |
| |
| RGBController for MSI laptop SteelSeries RGB devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBControllerKeyNames.h"
#include "RGBController_MSILaptop.h"
#define NA 0xFFFFFFFF
/**------------------------------------------------------------------*\
@name MSI Laptop SteelSeries RGB
@category Keyboard
@type USB
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectMSILaptop
@comment
\*-------------------------------------------------------------------*/
RGBController_MSILaptop::RGBController_MSILaptop(MSILaptopController* controller_ptr, const MSILaptopModel* model_ptr)
{
controller = controller_ptr;
model = model_ptr;
name = controller->GetDeviceName();
vendor = "SteelSeries";
description = std::string(model->sys_vendor) + " " + std::string(model->product_name) + " RGB Device";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
type = (controller->GetDeviceType() == MSI_LAPTOP_KLC) ? DEVICE_TYPE_KEYBOARD : DEVICE_TYPE_LEDSTRIP;
mode Direct;
Direct.name = "Direct";
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
}
RGBController_MSILaptop::~RGBController_MSILaptop()
{
for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
if(zones[zone_idx].matrix_map != NULL)
{
delete zones[zone_idx].matrix_map;
}
}
delete controller;
}
void RGBController_MSILaptop::SetupZones()
{
if(controller->GetDeviceType() == MSI_LAPTOP_KLC)
{
zone keyboard_zone;
keyboard_zone.name = ZONE_EN_KEYBOARD;
keyboard_zone.type = ZONE_TYPE_MATRIX;
keyboard_zone.leds_min = model->klc_leds_count;
keyboard_zone.leds_max = model->klc_leds_count;
keyboard_zone.leds_count = model->klc_leds_count;
keyboard_zone.matrix_map = new matrix_map_type;
keyboard_zone.matrix_map->height = model->klc_matrix_height;
keyboard_zone.matrix_map->width = model->klc_matrix_width;
keyboard_zone.matrix_map->map = (unsigned int *)model->klc_matrix_map;
zones.push_back(keyboard_zone);
for(unsigned int led_idx = 0; led_idx < model->klc_leds_count; led_idx++)
{
led new_led;
new_led.name = model->klc_leds[led_idx].name;
new_led.value = model->klc_leds[led_idx].id;
leds.push_back(new_led);
}
}
else
{
zone lightbar_zone;
lightbar_zone.name = "Lightbar";
lightbar_zone.type = ZONE_TYPE_LINEAR;
lightbar_zone.start_idx = 0;
lightbar_zone.leds_min = model->alc_lightbar_leds;
lightbar_zone.leds_max = model->alc_lightbar_leds;
lightbar_zone.leds_count = model->alc_lightbar_leds;
lightbar_zone.matrix_map = NULL;
zones.push_back(lightbar_zone);
zone logo_zone;
logo_zone.name = "Logo";
logo_zone.type = ZONE_TYPE_SINGLE;
logo_zone.start_idx = model->alc_lightbar_leds;
logo_zone.leds_min = 1;
logo_zone.leds_max = 1;
logo_zone.leds_count = 1;
logo_zone.matrix_map = NULL;
zones.push_back(logo_zone);
for(unsigned int led_idx = 0; led_idx < model->alc_leds_count; led_idx++)
{
led new_led;
new_led.name = model->alc_leds[led_idx].name;
new_led.value = model->alc_leds[led_idx].id;
leds.push_back(new_led);
}
}
SetupColors();
}
void RGBController_MSILaptop::ResizeZone(int /*zone*/, int /*new_size*/)
{
}
void RGBController_MSILaptop::DeviceUpdateLEDs()
{
controller->SetLEDs(leds, colors);
}
void RGBController_MSILaptop::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSILaptop::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSILaptop::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
@@ -0,0 +1,34 @@
/*---------------------------------------------------------*\
| RGBController_MSILaptop.h |
| |
| RGBController for MSI laptop SteelSeries RGB devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "MSILaptopController.h"
class RGBController_MSILaptop : public RGBController
{
public:
RGBController_MSILaptop(MSILaptopController* controller_ptr, const MSILaptopModel* model_ptr);
~RGBController_MSILaptop();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
MSILaptopController* controller;
const MSILaptopModel* model;
};
@@ -0,0 +1,121 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| |
| RGBController for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 June 4 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <string.h>
#include "MSIMonitorController.h"
#include "StringUtils.h"
MSIMonitorController::MSIMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name)
{
dev = dev_handle;
location = info.path;
name = dev_name;
}
MSIMonitorController::~MSIMonitorController()
{
hid_close(dev);
}
std::string MSIMonitorController::GetDeviceLocation()
{
return("HID: " + location);
}
std::string MSIMonitorController::GetNameString()
{
return(name);
}
std::string MSIMonitorController::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 MSIMonitorController::Set(uint8_t mode_value, const std::vector<RGBColor> colors, uint8_t last_bit)
{
/*---------------------------------------------------------*\
| Prepare color data |
\*---------------------------------------------------------*/
uint8_t data[MSI_MONITOR_PACKET_SIZE];
memset(data, 0x00, MSI_MONITOR_PACKET_SIZE);
unsigned int offset = 0;
data[offset++] = 0x71;
data[offset++] = 0x01;
for(int i = 0; i < 3; i++)
{
data[offset++] = 0x00;
}
data[offset++] = 0x01;
data[offset++] = 0x64;
for(int i = 0; i < 5; i++)
{
data[offset++] = 0x00;
}
/*---------------------------------------------------------*\
| put mode_value |
\*---------------------------------------------------------*/
data[offset++] = mode_value;
for(int i = 0; i < 3; i++)
{
data[offset++] = 0x00;
}
data[offset++] = 0x01;
data[offset++] = 0x64;
for(int i = 0; i < 5; i++)
{
data[offset++] = 0x00;
}
/*-----------------------------------------------------------------------*\
| this data looks like placeholder for additional LEDs in other monitors |
\*-----------------------------------------------------------------------*/
for(int i = 0; i < 9; i++)
{
data[offset++] = 0xff;
data[offset++] = 0x00;
data[offset++] = 0x00;
}
//RGB values begin
for(const RGBColor color: colors)
{
data[offset++] = RGBGetRValue(color);
data[offset++] = RGBGetGValue(color);
data[offset++] = RGBGetBValue(color);
}
/*-------------------------------------------------------------------------------*\
| last bit is probably a write to device bit- 0x01 saves to device, 0x00 doesn't. |
| For direct mode, bit is set to 0x00, otherwise lights will flicker |
\*-------------------------------------------------------------------------------*/
data[offset++] = last_bit;
/*---------------------------------------------------------*\
| Send the data (1 packet) |
\*---------------------------------------------------------*/
hid_send_feature_report(dev, data, MSI_MONITOR_PACKET_SIZE);
}
@@ -0,0 +1,53 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| |
| RGBController for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 June 1 |
| |
| 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 MSI_MONITOR_LEDS 9
#define MSI_MONITOR_PACKET_SIZE 78
enum
{
MSI_MONITOR_OFF_MODE_VALUE = 0x00,
MSI_MONITOR_STATIC_MODE_VALUE = 0x01,
MSI_MONITOR_BREATHING_MODE_VALUE = 0x02,
MSI_MONITOR_FLASHING_MODE_VALUE = 0x03,
MSI_MONITOR_LIGHTNING_MODE_VALUE = 0x05,
MSI_MONITOR_MARQUEE_MODE_VALUE = 0x06,
MSI_MONITOR_METEOR_MODE_VALUE = 0x08,
MSI_MONITOR_RAINBOW_MODE_VALUE = 0x1A,
MSI_MONITOR_RANDOM_MODE_VALUE = 0x1F
};
class MSIMonitorController
{
public:
MSIMonitorController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name);
~MSIMonitorController();
std::string GetDeviceLocation();
std::string GetFirmwareVersion();
std::string GetNameString();
std::string GetSerialString();
void Set(uint8_t mode_value, const std::vector<RGBColor> colors, uint8_t last_bit);
private:
hid_device *dev;
std::string description;
std::string location;
std::string name;
std::string version;
};
@@ -0,0 +1,38 @@
/*---------------------------------------------------------*\
| MSIKeyboardControllerDetect.cpp |
| |
| Detector for MSI monitor (MSI Gaming Controller) |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "MSIMonitorController.h"
#include "RGBController_MSIMonitor.h"
#define MSI_USB_VID 0x1462
#define MSI_USB_PID 0x3FA4
/*----------------------------------------------------------*\
| |
| DetectMSIMonitorController |
| |
| Detect MSI monitor and maybe others |
| |
\*----------------------------------------------------------*/
static void DetectMSIMonitorController(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
MSIMonitorController* controller = new MSIMonitorController(dev, *info, name);
RGBController_MSIMonitor* rgb_controller = new RGBController_MSIMonitor(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_HID_DETECTOR_IPU("MSI Monitor (Gaming Controller)", DetectMSIMonitorController, MSI_USB_VID, MSI_USB_PID, 0, 0x01, 0);
@@ -0,0 +1,171 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| |
| RGBController for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 June 1 |
| |
| This file is part of the OpenRGB project |
| 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
@type USB
@save :robot:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectMSIMonitorController
@comment Developed with MSI MAG272CQR
\*-------------------------------------------------------------------*/
RGBController_MSIMonitor::RGBController_MSIMonitor(MSIMonitorController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "MSI";
type = DEVICE_TYPE_MONITOR;
description = "MSI Monitor (Gaming Controller)";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
mode Direct;
Direct.name = "Direct";
Direct.value = MSI_MONITOR_STATIC_MODE_VALUE;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = MSI_MONITOR_STATIC_MODE_VALUE;
Static.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Static.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Static);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = MSI_MONITOR_BREATHING_MODE_VALUE;
Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Breathing.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Breathing);
mode Flashing;
Flashing.name = "Flashing";
Flashing.value = MSI_MONITOR_FLASHING_MODE_VALUE;
Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Flashing.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Flashing);
mode Lightning;
Lightning.name = "Lightning";
Lightning.value = MSI_MONITOR_LIGHTNING_MODE_VALUE;
Lightning.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Lightning.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Lightning);
mode Marquee;
Marquee.name = "Marquee";
Marquee.value = MSI_MONITOR_MARQUEE_MODE_VALUE;
Marquee.flags = MODE_FLAG_AUTOMATIC_SAVE;
Marquee.color_mode = MODE_COLORS_NONE;
modes.push_back(Marquee);
mode Meteor;
Meteor.name = "Meteor";
Meteor.value = MSI_MONITOR_METEOR_MODE_VALUE;
Meteor.flags = MODE_FLAG_AUTOMATIC_SAVE;
Meteor.color_mode = MODE_COLORS_NONE;
modes.push_back(Meteor);
mode Rainbow;
Rainbow.name = "Rainbow";
Rainbow.value = MSI_MONITOR_RAINBOW_MODE_VALUE;
Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE;
Rainbow.color_mode = MODE_COLORS_NONE;
modes.push_back(Rainbow);
mode Random;
Random.name = "Random";
Random.value = MSI_MONITOR_RANDOM_MODE_VALUE;
Random.flags = MODE_FLAG_AUTOMATIC_SAVE;
Random.color_mode = MODE_COLORS_RANDOM;
modes.push_back(Random);
mode Off;
Off.name = "Off";
Off.value = MSI_MONITOR_OFF_MODE_VALUE;
Off.flags = MODE_FLAG_AUTOMATIC_SAVE;
Off.color_mode = MODE_COLORS_NONE;
modes.push_back(Off);
SetupZones();
}
RGBController_MSIMonitor::~RGBController_MSIMonitor()
{
keepalive_thread_run = 0;
keepalive_thread->join();
delete keepalive_thread;
delete controller;
}
void RGBController_MSIMonitor::SetupZones()
{
zone new_zone;
new_zone.name = "Rear";
new_zone.type = ZONE_TYPE_LINEAR;
new_zone.leds_min = 9;
new_zone.leds_max = 9;
new_zone.leds_count = 9;
new_zone.matrix_map = nullptr;
zones.emplace_back(new_zone);
for(unsigned int i = 0 ; i < 9; i ++)
{
led new_led;
new_led.name = "LED " + std::to_string(i + 1);
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_MSIMonitor::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_MSIMonitor::DeviceUpdateLEDs()
{
last_update_time = std::chrono::steady_clock::now();
controller->Set(modes[active_mode].value, colors, active_mode == 0 ? 0x00 : 0x01);
}
void RGBController_MSIMonitor::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSIMonitor::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSIMonitor::DeviceUpdateMode()
{
controller->Set(modes[active_mode].value, colors, 0x01);
}
@@ -0,0 +1,38 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| |
| RGBController for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 May 16 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <chrono>
#include "RGBController.h"
#include "MSIMonitorController.h"
class RGBController_MSIMonitor : public RGBController
{
public:
RGBController_MSIMonitor(MSIMonitorController* controller_ptr);
~RGBController_MSIMonitor();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
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,523 @@
/*---------------------------------------------------------*\
| RGBController_RealtekARGB.cpp |
| |
| RGBController for Realtek USB ARGB ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_RealtekARGB.h"
/**------------------------------------------------------------------*\
@name Realtek ARGB Device
@category LEDStrip
@type USB
@save :x:
@direct :rotating_light:
@effects :white_check_mark:
@detectors RealtekARGBControllerDetect
@comment
\*-------------------------------------------------------------------*/
RGBController_RealtekARGB::RGBController_RealtekARGB(RealtekARGBController* controller_ptr)
{
controller = controller_ptr;
name = controller_ptr->get_dev_name();
vendor = controller_ptr->get_manu_name();
location = controller_ptr->get_dev_loc();
serial = controller_ptr->get_sn();
version = controller_ptr->get_fw_ver();
description = vendor + "ARGB Device";
type = DEVICE_TYPE_LEDSTRIP;
std::fill(std::begin(ready_to_reboot), std::end(ready_to_reboot), false);
SetupModes();
SetupZones();
}
void RGBController_RealtekARGB::SetupModes()
{
int brightness = controller->get_argb_brightness(0) >> 8;
mode Direct;
Direct.name = "Direct";
Direct.value = REALTEK_ARGB_EFF_NULL;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.brightness_min = 0;
Direct.brightness_max = 255;
Direct.brightness = brightness;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = REALTEK_ARGB_EFF_ALWAYS_ON;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Static.colors_min = 1;
Static.colors_max = 1;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.colors.resize(1);
Static.brightness_min = 0;
Static.brightness_max = 255;
Static.brightness = brightness;
modes.push_back(Static);
mode Blink;
Blink.name = "Blink";
Blink.value = REALTEK_ARGB_EFF_BLINK;
Blink.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Blink.speed_min = REALTEK_ARGB_SPEED_MIN;
Blink.speed_max = REALTEK_ARGB_SPEED_MAX;
Blink.speed = REALTEK_ARGB_SPEED_NORMAL;
Blink.colors_min = 1;
Blink.colors_max = 2;
Blink.color_mode = MODE_COLORS_MODE_SPECIFIC;
Blink.colors.resize(2);
Blink.brightness_min = 0;
Blink.brightness_max = 255;
Blink.brightness = brightness;
modes.push_back(Blink);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = REALTEK_ARGB_EFF_BREATH;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Breathing.speed_min = REALTEK_ARGB_SPEED_MIN;
Breathing.speed_max = REALTEK_ARGB_SPEED_MAX;
Breathing.speed = REALTEK_ARGB_SPEED_NORMAL;
Breathing.colors_min = 1;
Breathing.colors_max = 2;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
Breathing.colors.resize(2);
Breathing.brightness_min = 0;
Breathing.brightness_max = 255;
Breathing.brightness = brightness;
modes.push_back(Breathing);
mode Spectrum;
Spectrum.name = "Spectrum";
Spectrum.value = REALTEK_ARGB_EFF_SPECTRUM;
Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Spectrum.speed_min = REALTEK_ARGB_SPEED_MIN;
Spectrum.speed_max = REALTEK_ARGB_SPEED_MAX;
Spectrum.speed = REALTEK_ARGB_SPEED_NORMAL;
Spectrum.color_mode = MODE_COLORS_NONE;
Spectrum.brightness_min = 0;
Spectrum.brightness_max = 255;
Spectrum.brightness = brightness;
modes.push_back(Spectrum);
mode Scroll;
Scroll.name = "Scroll";
Scroll.value = REALTEK_ARGB_EFF_SCROLL;
Scroll.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Scroll.speed_min = REALTEK_ARGB_SPEED_MIN;
Scroll.speed_max = REALTEK_ARGB_SPEED_MAX;
Scroll.speed = REALTEK_ARGB_SPEED_NORMAL;
Scroll.colors_min = 1;
Scroll.colors_max = 2;
Scroll.color_mode = MODE_COLORS_MODE_SPECIFIC;
Scroll.colors.resize(2);
Scroll.brightness_min = 0;
Scroll.brightness_max = 255;
Scroll.brightness = brightness;
modes.push_back(Scroll);
mode RainbowScroll;
RainbowScroll.name = "Rainbow Scroll";
RainbowScroll.value = REALTEK_ARGB_EFF_RAINBOW_SCROLL;
RainbowScroll.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
RainbowScroll.speed_min = REALTEK_ARGB_SPEED_MIN;
RainbowScroll.speed_max = REALTEK_ARGB_SPEED_MAX;
RainbowScroll.speed = REALTEK_ARGB_SPEED_NORMAL;
RainbowScroll.color_mode = MODE_COLORS_NONE;
RainbowScroll.brightness_min = 0;
RainbowScroll.brightness_max = 255;
RainbowScroll.brightness = brightness;
modes.push_back(RainbowScroll);
mode RunningWater;
RunningWater.name = "Running Water";
RunningWater.value = REALTEK_ARGB_EFF_RUNNING_WATER;
RunningWater.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
RunningWater.speed_min = REALTEK_ARGB_SPEED_MIN;
RunningWater.speed_max = REALTEK_ARGB_SPEED_MAX;
RunningWater.speed = REALTEK_ARGB_SPEED_NORMAL;
RunningWater.colors_min = 1;
RunningWater.colors_max = 2;
RunningWater.color_mode = MODE_COLORS_MODE_SPECIFIC;
RunningWater.colors.resize(2);
RunningWater.brightness_min = 0;
RunningWater.brightness_max = 255;
RunningWater.brightness = brightness;
modes.push_back(RunningWater);
mode Sliding;
Sliding.name = "Sliding";
Sliding.value = REALTEK_ARGB_EFF_SLIDING;
Sliding.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR;
Sliding.speed_min = REALTEK_ARGB_SPEED_MIN;
Sliding.speed_max = REALTEK_ARGB_SPEED_MAX;
Sliding.speed = REALTEK_ARGB_SPEED_NORMAL;
Sliding.colors_min = 1;
Sliding.colors_max = 2;
Sliding.color_mode = MODE_COLORS_MODE_SPECIFIC;
Sliding.colors.resize(2);
Sliding.brightness_min = 0;
Sliding.brightness_max = 255;
Sliding.brightness = brightness;
modes.push_back(Sliding);
mode WideSliding;
WideSliding.name = "Wide Sliding";
WideSliding.value = REALTEK_ARGB_EFF_WIDE_SLIDING;
WideSliding.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR;
WideSliding.speed_min = REALTEK_ARGB_SPEED_MIN;
WideSliding.speed_max = REALTEK_ARGB_SPEED_MAX;
WideSliding.speed = REALTEK_ARGB_SPEED_NORMAL;
WideSliding.colors_min = 1;
WideSliding.colors_max = 2;
WideSliding.color_mode = MODE_COLORS_MODE_SPECIFIC;
WideSliding.colors.resize(2);
WideSliding.brightness_min = 0;
WideSliding.brightness_max = 255;
WideSliding.brightness = brightness;
modes.push_back(WideSliding);
mode RainbowSliding;
RainbowSliding.name = "Rainbow Sliding";
RainbowSliding.value = REALTEK_ARGB_EFF_RAINBOW_SLIDING;
RainbowSliding.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR;
RainbowSliding.speed_min = REALTEK_ARGB_SPEED_MIN;
RainbowSliding.speed_max = REALTEK_ARGB_SPEED_MAX;
RainbowSliding.speed = REALTEK_ARGB_SPEED_NORMAL;
RainbowSliding.color_mode = MODE_COLORS_NONE;
RainbowSliding.brightness_min = 0;
RainbowSliding.brightness_max = 255;
RainbowSliding.brightness = brightness;
modes.push_back(RainbowSliding);
mode RainbowFadeSliding;
RainbowFadeSliding.name = "Rainbow Fade Sliding";
RainbowFadeSliding.value = REALTEK_ARGB_EFF_RAINBOW_FADE_SLIDING;
RainbowFadeSliding.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
RainbowFadeSliding.speed_min = REALTEK_ARGB_SPEED_MIN;
RainbowFadeSliding.speed_max = REALTEK_ARGB_SPEED_MAX;
RainbowFadeSliding.speed = REALTEK_ARGB_SPEED_NORMAL;
RainbowFadeSliding.color_mode = MODE_COLORS_NONE;
RainbowFadeSliding.brightness_min = 0;
RainbowFadeSliding.brightness_max = 255;
RainbowFadeSliding.brightness = brightness;
modes.push_back(RainbowFadeSliding);
mode NewtonCradle;
NewtonCradle.name = "Newton Cradle";
NewtonCradle.value = REALTEK_ARGB_EFF_NEWTON_CRADLE;
NewtonCradle.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
NewtonCradle.speed_min = REALTEK_ARGB_SPEED_MIN;
NewtonCradle.speed_max = REALTEK_ARGB_SPEED_MAX;
NewtonCradle.speed = REALTEK_ARGB_SPEED_NORMAL;
NewtonCradle.colors_min = 1;
NewtonCradle.colors_max = 2;
NewtonCradle.color_mode = MODE_COLORS_MODE_SPECIFIC;
NewtonCradle.colors.resize(2);
NewtonCradle.brightness_min = 0;
NewtonCradle.brightness_max = 255;
NewtonCradle.brightness = brightness;
modes.push_back(NewtonCradle);
mode Meteor;
Meteor.name = "Meteor";
Meteor.value = REALTEK_ARGB_EFF_METEOR;
Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Meteor.speed_min = REALTEK_ARGB_SPEED_MIN;
Meteor.speed_max = REALTEK_ARGB_SPEED_MAX;
Meteor.speed = REALTEK_ARGB_SPEED_NORMAL;
Meteor.colors_min = 1;
Meteor.colors_max = 2;
Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC;
Meteor.colors.resize(2);
Meteor.brightness_min = 0;
Meteor.brightness_max = 255;
Meteor.brightness = brightness;
modes.push_back(Meteor);
mode ZigZag;
ZigZag.name = "ZigZag";
ZigZag.value = REALTEK_ARGB_EFF_ZIGZAG;
ZigZag.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR;
ZigZag.speed_min = REALTEK_ARGB_SPEED_MIN;
ZigZag.speed_max = REALTEK_ARGB_SPEED_MAX;
ZigZag.speed = REALTEK_ARGB_SPEED_NORMAL;
ZigZag.colors_min = 1;
ZigZag.colors_max = 2;
ZigZag.color_mode = MODE_COLORS_MODE_SPECIFIC;
ZigZag.colors.resize(2);
ZigZag.brightness_min = 0;
ZigZag.brightness_max = 255;
ZigZag.brightness = brightness;
modes.push_back(ZigZag);
mode StarryNight;
StarryNight.name = "Starry Night";
StarryNight.value = REALTEK_ARGB_EFF_STARRY_NIGHT;
StarryNight.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR;
StarryNight.speed_min = REALTEK_ARGB_SPEED_MIN;
StarryNight.speed_max = REALTEK_ARGB_SPEED_MAX;
StarryNight.speed = REALTEK_ARGB_SPEED_NORMAL;
StarryNight.colors_min = 1;
StarryNight.colors_max = 1;
StarryNight.color_mode = MODE_COLORS_MODE_SPECIFIC;
StarryNight.colors.resize(1);
StarryNight.brightness_min = 0;
StarryNight.brightness_max = 255;
StarryNight.brightness = brightness;
modes.push_back(StarryNight);
mode Stack;
Stack.name = "Stack";
Stack.value = REALTEK_ARGB_EFF_STACK;
Stack.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Stack.colors_min = 1;
Stack.colors_max = 2;
Stack.color_mode = MODE_COLORS_MODE_SPECIFIC;
Stack.colors.resize(2);
Stack.brightness_min = 0;
Stack.brightness_max = 255;
Stack.brightness = brightness;
modes.push_back(Stack);
}
RGBController_RealtekARGB::~RGBController_RealtekARGB()
{
delete controller;
}
void RGBController_RealtekARGB::SetupZones()
{
/*-------------------------------------------------*\
| Only set LED count on the first run |
\*-------------------------------------------------*/
bool first_run = false;
int idx = 0;
int argb_num = 0;
int fix_grps = 0;
int num_fixgrp = 0;
int argb_num_fixgrp = 0;
if(zones.size() == 0)
{
first_run = true;
}
/*-------------------------------------------------*\
| Clear any existing color/LED configuration |
\*-------------------------------------------------*/
leds.clear();
colors.clear();
valid_grp.clear();
fix_grps = controller->get_fix_grps();
for(int grp_num = 0; grp_num < REALTEK_ARGB_NUM_ARGB_GRP; grp_num++)
{
if(controller->get_zone_enable(grp_num))
{
valid_grp.push_back(grp_num);
if(fix_grps & (0x1 << grp_num))
{
num_fixgrp++;
argb_num_fixgrp += controller->get_argb_num(grp_num);
}
}
}
/*-------------------------------------------------*\
| Set zones and leds |
\*-------------------------------------------------*/
zones.resize(valid_grp.size());
for(int grp_num : valid_grp)
{
argb_num = controller->get_argb_num(grp_num);
zones[idx].name = "strip " + std::to_string(idx + 1);
zones[idx].type = ZONE_TYPE_LINEAR;
if(fix_grps & (0x1 << grp_num))
{
zones[idx].leds_count = argb_num;
zones[idx].leds_min = argb_num;
zones[idx].leds_max = argb_num;
}
else
{
if(first_run)
{
zones[idx].leds_count = 0;
}
else
{
zones[idx].leds_count = argb_num;
}
zones[idx].leds_min = 0;
zones[idx].leds_max = (REALTEK_ARGB_MAX - argb_num_fixgrp) / ((int)valid_grp.size() - num_fixgrp);
}
zones[idx].matrix_map = NULL;
for(unsigned int led_idx = 0; led_idx < zones[idx].leds_count; led_idx++)
{
led myled;
myled.name = zones[idx].name + " led_";
myled.name.append(std::to_string(led_idx + 1));
leds.push_back(myled);
}
idx++;
}
SetupColors();
}
void RGBController_RealtekARGB::ResizeZone(int zone, int new_size)
{
int fix_grps = controller->get_fix_grps();
int orig_num = controller->get_argb_num(valid_grp[zone]);
bool need_reboot = true;
if((size_t) zone >= zones.size())
{
return;
}
if((new_size == orig_num) && zones[zone].leds_count)
{
return;
}
if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max))
{
int total_argb_num = 0;
std::vector<RGBColor> rtk_colors(orig_num, 0);
for(int grp_num = 0; grp_num < REALTEK_ARGB_NUM_ARGB_GRP; grp_num++)
{
if(grp_num == valid_grp[zone])
{
total_argb_num += new_size;
}
else
{
total_argb_num += controller->get_argb_num(grp_num);
}
}
if(total_argb_num > REALTEK_ARGB_MAX)
{
return;
}
controller->set_argb_direct(valid_grp[zone], rtk_colors, 0xFF);
controller->set_argb_num(valid_grp[zone], new_size);
ready_to_reboot[valid_grp[zone]] = true;
for(int grp_num = 0; grp_num < REALTEK_ARGB_NUM_ARGB_GRP; grp_num++)
{
if(!ready_to_reboot[grp_num] && !(fix_grps & (0x1 << grp_num)))
{
need_reboot = false;
break;
}
}
if(need_reboot)
{
controller->device_reboot();
controller->device_rescan_trigger();
}
SetupZones();
}
}
void RGBController_RealtekARGB::DeviceUpdateLEDs()
{
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
if(zones[zone_idx].leds_count > 0)
{
UpdateZoneLEDs((int)zone_idx);
}
}
}
void RGBController_RealtekARGB::UpdateZoneLEDs(int zone)
{
unsigned short brightness = 0xFF;
mode& curr_mode = modes[active_mode];
std::vector<RGBColor> color_buf;
if(curr_mode.color_mode == MODE_COLORS_PER_LED &&
curr_mode.value == REALTEK_ARGB_EFF_NULL) //direct mode
{
if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS)
{
brightness = curr_mode.brightness;
}
color_buf.resize(zones[zone].leds_count);
for(unsigned int i = 0; i < zones[zone].leds_count; i++)
color_buf[i] = zones[zone].colors[i];
controller->set_argb_direct(valid_grp[zone], color_buf, brightness);
}
else
{
UpdateSingleLED(zone);
}
}
void RGBController_RealtekARGB::UpdateSingleLED(int zone)
{
mode& curr_mode = modes[active_mode];
std::vector<RGBColor> rtk_colors = curr_mode.colors;
struct RealtekARGBControllerSetEffParam param;
param.speed = REALTEK_ARGB_SPEED_NORMAL;
param.brightness = 0xFF;
param.dir = 0;
param.random_color = 0;
if(curr_mode.flags & MODE_FLAG_HAS_SPEED)
{
param.speed = curr_mode.speed;
}
if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS)
{
param.brightness = curr_mode.brightness;
}
if(curr_mode.flags & MODE_FLAG_HAS_DIRECTION_LR)
{
if(curr_mode.direction == MODE_DIRECTION_RIGHT)
{
param.dir = 1;
}
}
if(curr_mode.flags & MODE_FLAG_HAS_RANDOM_COLOR)
{
if(curr_mode.color_mode == MODE_COLORS_RANDOM)
{
param.random_color = 1;
}
}
if(curr_mode.color_mode == MODE_COLORS_PER_LED)
{
rtk_colors = colors;
}
else if(curr_mode.color_mode == MODE_COLORS_NONE)
{
rtk_colors.clear();
}
controller->set_argb_effect(valid_grp[zone], curr_mode.value, rtk_colors, &param);
}
void RGBController_RealtekARGB::DeviceUpdateMode()
{
if(modes[active_mode].value != REALTEK_ARGB_EFF_NULL)
{
DeviceUpdateLEDs();
}
}
@@ -0,0 +1,36 @@
/*---------------------------------------------------------*\
| RGBController_RealtekARGB.h |
| |
| RGBController for Realtek USB ARGB ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "RealtekARGBController.h"
class RGBController_RealtekARGB : public RGBController
{
public:
RGBController_RealtekARGB(RealtekARGBController* controller_ptr);
~RGBController_RealtekARGB();
void SetupModes();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int zone);
void DeviceUpdateMode();
private:
RealtekARGBController* controller;
std::vector<int> valid_grp;
bool ready_to_reboot[REALTEK_ARGB_NUM_ARGB_GRP] = {false};
};
@@ -0,0 +1,715 @@
/*---------------------------------------------------------*\
| RealtekARGBController.cpp |
| |
| Controller for Realtek USB ARGB ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RealtekARGBController.h"
#include "MathUtils.h"
#include "StringUtils.h"
using namespace std::chrono_literals;
static const unsigned char hid_set_packet[] =
{
0x56, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xE3,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char hid_get_packet[] =
{
0x56, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x10, 0xE2,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char hid_end_packet[] =
{
0x58, 0x53, 0x42, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
RealtekARGBController::RealtekARGBController(hid_device* dev, hid_device_info* info)
{
hdev = dev;
hidinfo = info;
for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++)
{
argbctl_data[i] = (unsigned char*)calloc(REALTEK_ARGB_CTL_DATA_SIZE, 1);
memset(argbctl_data[i], 0, REALTEK_ARGB_CTL_DATA_SIZE);
prev_bright[i] = 0xFFFF;
}
device_init();
keepalive_thread_run = false;
keepalive_thread = NULL;
}
RealtekARGBController::~RealtekARGBController()
{
/*-----------------------------------------------------*\
| Close keepalive thread |
\*-----------------------------------------------------*/
if(keepalive_thread != NULL)
{
keepalive_thread_run = false;
keepalive_thread->join();
delete keepalive_thread;
}
if(hdev)
{
for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++)
{
int ret = set_appctl(i, REALTEK_ARGB_LED_CTL_FW);
if(!ret)
{
appctl[i] = REALTEK_ARGB_LED_CTL_FW;
}
}
hid_close(hdev);
hdev = NULL;
}
memset(argbctl_hdr, 0, REALTEK_ARGB_CTL_HDR_SIZE);
for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++)
free(argbctl_data[i]);
}
void RealtekARGBController::KeepaliveThreadFunction()
{
/*-----------------------------------------------------------------*\
| One shot thread to rescan device |
\*-----------------------------------------------------------------*/
while(keepalive_thread_run.load())
{
std::this_thread::sleep_for(2s);
device_rescan();
keepalive_thread_run = false;
}
}
int RealtekARGBController::usb_hid_ioctl(unsigned char* usb_buf, unsigned char* data, int data_len,
unsigned int offset, unsigned char is_in)
{
int id;
int ret = 0;
int buf_len = usb_hid_get_report(data_len, &id);
if(hdev == NULL)
{
free(usb_buf);
return -1;
}
if(!is_in && data_len)
{
usb_buf[0x04] = data[0];
}
memcpy(&usb_buf[0x08], &data_len, sizeof(data_len));
memcpy(&usb_buf[0x1B], &data_len, sizeof(data_len));
ret = hid_send_feature_report(hdev, usb_buf, sizeof(hid_get_packet));
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, data, data_len);
usb_buf[0x00] = id;
if(is_in)
{
ret = hid_get_feature_report(hdev, usb_buf, buf_len);
memcpy(data, usb_buf + offset, data_len);
}
else
{
ret = hid_send_feature_report(hdev, usb_buf, buf_len);
}
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_end_packet, sizeof(hid_end_packet));
ret = hid_get_feature_report(hdev, usb_buf, sizeof(hid_end_packet));
free(usb_buf);
return (ret > 0) ? 0 : ret;
}
int RealtekARGBController::usb_hid_get_report(int data_len, int* id)
{
int retlen = 0;
if(data_len <= REALTEK_ARGB_HID_DATALEN_CH2)
{
*id = REALTEK_ARGB_HID_ID_DATA_CH2;
retlen = REALTEK_ARGB_HID_DATALEN_CH2;
}
else if(data_len <= REALTEK_ARGB_HID_DATALEN_CH3)
{
*id = REALTEK_ARGB_HID_ID_DATA_CH3;
retlen = REALTEK_ARGB_HID_DATALEN_CH3;
}
else if(data_len <= REALTEK_ARGB_HID_DATALEN_CH4)
{
*id = REALTEK_ARGB_HID_ID_DATA_CH4;
retlen = REALTEK_ARGB_HID_DATALEN_CH4;
}
else if(data_len <= REALTEK_ARGB_HID_DATALEN_CH5)
{
*id = REALTEK_ARGB_HID_ID_DATA_CH5;
retlen = REALTEK_ARGB_HID_DATALEN_CH5;
}
else
{
*id = REALTEK_ARGB_HID_ID_DATA_CH1;
retlen = REALTEK_ARGB_HID_DATALEN_CH1;
}
return retlen;
}
void RealtekARGBController::device_init()
{
set_write_unlock();
get_argbctl_hdr();
get_argbctl_data();
for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++)
{
int ret = set_appctl(i, REALTEK_ARGB_LED_CTL_FW);
if(!ret)
{
appctl[i] = REALTEK_ARGB_LED_CTL_FW;
}
}
}
int RealtekARGBController::set_write_unlock()
{
int ret = 0;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
int data_len = 96;
unsigned int addr = 0xAC004000;
unsigned char* data = (unsigned char*)calloc(data_len, 1);
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0x92;
memcpy(&usb_buf[0x17], &addr, sizeof(addr));
ret = usb_hid_ioctl(usb_buf, data, data_len, 0, true);
free(data);
return ret;
}
unsigned char RealtekARGBController::get_support_openrgb()
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned char is_support = 0;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xCC;
usb_buf[0x14] = 0x04;
usb_buf[0x15] = REALTEK_ARGB_SYNC_METHOD_OPENRGB;
if(usb_hid_ioctl(usb_buf, &is_support, sizeof(is_support), 0, true))
{
is_support = 0;
}
return is_support;
}
std::string RealtekARGBController::get_manu_name()
{
return StringUtils::wchar_to_char(hidinfo->manufacturer_string);
}
std::string RealtekARGBController::get_product_name()
{
return StringUtils::wchar_to_char(hidinfo->product_string);
}
std::string RealtekARGBController::get_sn()
{
return StringUtils::wchar_to_char(hidinfo->serial_number);
}
std::string RealtekARGBController::get_dev_loc()
{
return hidinfo->path;
}
std::string RealtekARGBController::get_fw_ver()
{
std::string ver = "";
struct RealtekARGBControllerFWVersion fw_ver;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xA5;
if(!usb_hid_ioctl(usb_buf, (unsigned char*)&fw_ver, sizeof(fw_ver), 0, true))
{
ver += std::to_string(fw_ver.fw_major_ver) + "." +
std::to_string(fw_ver.fw_minor_ver) + "." +
std::to_string(fw_ver.fw_extra_ver) + "." +
std::to_string(fw_ver.fw_build_date);
}
return ver;
}
std::string RealtekARGBController::get_ic_uuid()
{
unsigned int uuid = 0;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xC3;
usb_hid_ioctl(usb_buf, (unsigned char*)&uuid, sizeof(uuid), 0, true);
return StringUtils::u32int_to_hexString(uuid);
}
std::string RealtekARGBController::get_dev_name()
{
bool got_custled = false;
std::string devname = get_product_name();
if(custled[0])
{
got_custled = true;
}
else
{
if(!get_custled(custled, sizeof(custled)))
{
got_custled = true;
}
}
if(got_custled)
{
if(custled[5] == REALTEK_ARGB_CUST_DEVNAME_MANU_UUID)
{
devname = get_manu_name() + get_ic_uuid();
}
}
return devname;
}
int RealtekARGBController::get_fix_grps()
{
bool got_custled = false;
static int fix_grps = 0;
if(custled[0])
{
got_custled = true;
}
else
{
if(!get_custled(custled, sizeof(custled)))
{
got_custled = true;
}
}
if(got_custled)
{
fix_grps = custled[4];
}
return fix_grps;
}
bool RealtekARGBController::get_zone_enable(int grp_num)
{
bool got_custled = false;
bool en = false;
if(custled[0])
{
got_custled = true;
}
else
{
if(!get_custled(custled, sizeof(custled)))
{
got_custled = true;
}
}
if(got_custled)
{
en = (custled[5] & (0x1 << grp_num)) ? true : false;
}
return en;
}
int RealtekARGBController::get_argb_num(int grp_num)
{
int num_rgb = 0;
memcpy(&num_rgb, &argbctl_hdr[32 + grp_num * 2], 2);
return num_rgb;
}
int RealtekARGBController::get_argb_brightness(int grp_num)
{
int bright = 0;
memcpy(&bright, &argbctl_hdr[42 + grp_num * 2], 2);
return bright;
}
int RealtekARGBController::set_argb_brightness(int grp_num, unsigned short bright)
{
memcpy(&argbctl_hdr[42 + grp_num * sizeof(bright)], &bright, sizeof(bright));
set_argbctl_hdr();
return 0;
}
int RealtekARGBController::set_appctl(unsigned char grp_num, unsigned char ctl_sts)
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x01;
usb_buf[0x15] = ctl_sts;
usb_buf[0x16] = grp_num;
return usb_hid_ioctl(usb_buf, &ctl_sts, sizeof(ctl_sts), 0, false);
}
int RealtekARGBController::get_custled(unsigned char* cust, unsigned int cust_len)
{
int ret = 0;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned int addr = 0xAC004000;
unsigned int offset = 0x800;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len + offset, 1);// will release in usb_hid_ioctl function
unsigned char* data = (unsigned char*)calloc(buf_len, 1);
memset(data, 0x00, buf_len);
memset(usb_buf, 0x00, buf_len + offset);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0x92;
memcpy(&usb_buf[0x17], &addr, sizeof(addr));
ret = usb_hid_ioctl(usb_buf, data, buf_len, offset, true);
if(!ret)
{
memcpy(cust, &data[70], cust_len);
}
free(data);
return ret;
}
int RealtekARGBController::get_argbctl_hdr()
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xCC;
usb_buf[0x14] = 0x02;
return usb_hid_ioctl(usb_buf, argbctl_hdr, REALTEK_ARGB_CTL_HDR_SIZE, 0, true);
}
int RealtekARGBController::set_argbctl_hdr()
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x02;
return usb_hid_ioctl(usb_buf, argbctl_hdr, REALTEK_ARGB_CTL_HDR_SIZE, 0, false);
}
int RealtekARGBController::get_argbctl_data()
{
int ret = 0;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
int offset = REALTEK_ARGB_CTL_HDR_SIZE;
unsigned char* usb_buf;
for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++)
{
usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xCC;
usb_buf[0x14] = 0x02;
memcpy(&usb_buf[0x17], &offset, sizeof(offset));
ret = usb_hid_ioctl(usb_buf, argbctl_data[i], REALTEK_ARGB_CTL_DATA_SIZE, 0, true);
offset += REALTEK_ARGB_CTL_DATA_SIZE;
}
return ret;
}
int RealtekARGBController::set_argbctl_data(unsigned char grp_num)
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
int offset = REALTEK_ARGB_CTL_HDR_SIZE + REALTEK_ARGB_CTL_DATA_SIZE * grp_num;
unsigned char* usb_buf;
usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x02;
memcpy(&usb_buf[0x17], &offset, sizeof(offset));
return usb_hid_ioctl(usb_buf, argbctl_data[grp_num], REALTEK_ARGB_CTL_DATA_SIZE, 0, false);
}
int RealtekARGBController::set_eff_id(unsigned char grp_num, unsigned short effid)
{
memcpy(argbctl_data[grp_num], &effid, sizeof(effid));
return 0;
}
int RealtekARGBController::set_p_color(unsigned char grp_num, RGBColor color)
{
memcpy(argbctl_data[grp_num] + 8, &color, sizeof(color));
return 0;
}
int RealtekARGBController::set_s_color(unsigned char grp_num, RGBColor color)
{
memcpy(argbctl_data[grp_num] + 12, &color, sizeof(color));
return 0;
}
int RealtekARGBController::set_cycle(unsigned char grp_num, unsigned short cycle)
{
memcpy(argbctl_data[grp_num] + 2, &cycle, sizeof(cycle));
return 0;
}
int RealtekARGBController::set_ramp(unsigned char grp_num, unsigned short ramp)
{
memcpy(argbctl_data[grp_num] + 4, &ramp, sizeof(ramp));
return 0;
}
int RealtekARGBController::set_stable(unsigned char grp_num, unsigned short stable)
{
memcpy(argbctl_data[grp_num] + 6, &stable, sizeof(stable));
return 0;
}
int RealtekARGBController::set_subcmd(unsigned char grp_num, int subcmd)
{
memcpy(argbctl_data[grp_num] + 16, &subcmd, sizeof(subcmd));
return 0;
}
int RealtekARGBController::set_direct(unsigned char* color, int color_num, unsigned char grp_num)
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
int data_len = color_num * REALTEK_ARGB_COLOR_DEPTH;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x03;
usb_buf[0x15] = REALTEK_ARGB_SYNC_METHOD_OPENRGB;
usb_buf[0x16] = grp_num;
memcpy(&usb_buf[0x17], &color_num, sizeof(color_num));
return usb_hid_ioctl(usb_buf, color, data_len, 0, false);
}
int RealtekARGBController::get_flash_argbctl_hdr(unsigned char* data)
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned int addr = 0xAC000000;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0x92;
memcpy(&usb_buf[0x17], &addr, sizeof(addr));
return usb_hid_ioctl(usb_buf, data, buf_len, 0, true);
}
int RealtekARGBController::set_flash_argbctl_hdr(unsigned int offset, unsigned int data_len)
{
int ret = 0;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned int addr = 0xAC000000;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function
unsigned char* data = (unsigned char*)calloc(buf_len, 1);
memset(data, 0, buf_len);
ret = get_flash_argbctl_hdr(data);
if(!ret)
{
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x10;
memcpy(&usb_buf[0x17], &addr, sizeof(addr));
ret = usb_hid_ioctl(usb_buf, (unsigned char*)&addr, sizeof(addr), 0, false);
usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x11;
memcpy(data + offset, argbctl_hdr + offset, data_len);
ret = usb_hid_ioctl(usb_buf, data, buf_len, 0, false);
}
free(data);
return ret;
}
int RealtekARGBController::set_argb_direct(int grp_num, std::vector<RGBColor> color_buf, unsigned short brightness)
{
int ret = -1;
size_t color_num = color_buf.size();
size_t buf_len = color_num * REALTEK_ARGB_COLOR_DEPTH;
unsigned char* buf;
std::lock_guard<std::mutex> lock(my_mutex);
if(color_num == 0)
{
goto exit;
}
if(appctl[grp_num] != REALTEK_ARGB_LED_CTL_APP)
{
ret = set_appctl(grp_num, REALTEK_ARGB_LED_CTL_APP);
if(ret)
{
goto exit;
}
else
{
appctl[grp_num] = REALTEK_ARGB_LED_CTL_APP;
}
}
if(prev_bright[grp_num] != brightness)
{
prev_bright[grp_num] = brightness;
ret = set_argb_brightness(grp_num, brightness << 8);
if(ret)
{
goto exit;
}
}
buf = (uint8_t*)malloc(buf_len);
memset(buf, 0, buf_len);
for(size_t i = 0; i < color_num; i++)
{
buf[i * REALTEK_ARGB_COLOR_DEPTH + 0] = RGBGetRValue(color_buf[i]);
buf[i * REALTEK_ARGB_COLOR_DEPTH + 1] = RGBGetGValue(color_buf[i]);
buf[i * REALTEK_ARGB_COLOR_DEPTH + 2] = RGBGetBValue(color_buf[i]);
}
ret = set_direct(buf, (int)color_num, grp_num);
free(buf);
exit:
return ret;
}
int RealtekARGBController::set_argb_effect(int grp_num, uint8_t mode, std::vector<RGBColor> color_buf, struct RealtekARGBControllerSetEffParam* param)
{
int ret = -1;
int cycle = MathUtils::IntInterpolate(REALTEK_ARGB_CYCLE_MAX, REALTEK_ARGB_CYCLE_MIN, 0, REALTEK_ARGB_SPEED_MAX, param->speed);
std::lock_guard<std::mutex> lock(my_mutex);
if(mode == REALTEK_ARGB_EFF_ALWAYS_ON || mode == REALTEK_ARGB_EFF_STACK)
{
cycle = 0;
}
if(color_buf.size() >= 1)
{
set_p_color(grp_num, color_buf[0]);
}
if(color_buf.size() >= 2)
{
set_s_color(grp_num, color_buf[1]);
}
set_eff_id(grp_num, mode);
set_cycle(grp_num, cycle);
set_ramp(grp_num, 0);
set_stable(grp_num, 0);
set_subcmd(grp_num, (param->dir || param->random_color) ? 0x1 : 0x0);
set_argb_brightness(grp_num, param->brightness << 8);
set_argbctl_data(grp_num);
ret = set_appctl(grp_num, REALTEK_ARGB_LED_CTL_FW);
if(!ret)
{
appctl[grp_num] = REALTEK_ARGB_LED_CTL_FW;
}
return ret;
}
int RealtekARGBController::set_argb_num(int grp_num, unsigned short new_num)
{
memcpy(&argbctl_hdr[32 + grp_num * sizeof(new_num)], &new_num, sizeof(new_num));
set_flash_argbctl_hdr(32 + grp_num * sizeof(new_num), sizeof(new_num));
return 0;
}
int RealtekARGBController::device_reboot()
{
std::lock_guard<std::mutex> lock(my_mutex);
int ret = 0;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
int reboot_type = 1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x30;
usb_buf[0x14] = reboot_type;
ret = usb_hid_ioctl(usb_buf, (unsigned char*)&reboot_type, sizeof(reboot_type), 0, false);
hid_close(hdev);
hdev = NULL;
return ret;
}
void RealtekARGBController::device_rescan_trigger()
{
keepalive_thread_run = true;
keepalive_thread = new std::thread(&RealtekARGBController::KeepaliveThreadFunction, this);
}
void RealtekARGBController::device_rescan()
{
hid_device_info* info_full = hid_enumerate(REALTEK_ARGB_VID, REALTEK_ARGB_PID);
hid_device_info* info_temp = info_full;
while(info_temp)
{
if(info_temp->vendor_id == REALTEK_ARGB_VID &&
info_temp->product_id == REALTEK_ARGB_PID &&
info_temp->usage == REALTEK_ARGB_HID2SCSI_USAGE &&
info_temp->usage_page == REALTEK_ARGB_HID2SCSI_PG)
{
hid_device* dev_temp = hid_open_path(info_temp->path);
if(dev_temp)
{
hdev = dev_temp;
if(get_support_openrgb())
{
device_init();
break;
}
else
{
hid_close(hdev);
hdev = NULL;
}
}
}
info_temp = info_temp->next;
}
hid_free_enumeration(info_full);
}
@@ -0,0 +1,182 @@
/*---------------------------------------------------------*\
| RealtekARGBController.h |
| |
| Controller for Realtek USB ARGB ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <cmath>
#include <cstring>
#include <vector>
#include <thread>
#include <hidapi.h>
#include "RGBController.h"
#define REALTEK_ARGB_VID 0x0BDA
#define REALTEK_ARGB_PID 0x9209
#define REALTEK_ARGB_HID2SCSI_PG 0xFF00
#define REALTEK_ARGB_HID2SCSI_USAGE 0x0001
#define REALTEK_ARGB_NUM_ARGB_GRP 5
#define REALTEK_ARGB_BRINTF_TYPE_HID 1
#define REALTEK_ARGB_SYNC_METHOD_OPENRGB 6
#define REALTEK_ARGB_COLOR_DEPTH 3
#define REALTEK_ARGB_MAX 400
#define REALTEK_ARGB_CTL_HDR_SIZE 80
#define REALTEK_ARGB_CTL_DATA_SIZE 96
#define REALTEK_ARGB_HID_DATALEN_CH1 4096
#define REALTEK_ARGB_HID_DATALEN_CH2 64
#define REALTEK_ARGB_HID_DATALEN_CH3 512
#define REALTEK_ARGB_HID_DATALEN_CH4 1200
#define REALTEK_ARGB_HID_DATALEN_CH5 2048
#define REALTEK_ARGB_HID_ID_DATA_CH1 0x57
#define REALTEK_ARGB_HID_ID_DATA_CH2 0x59
#define REALTEK_ARGB_HID_ID_DATA_CH3 0x5A
#define REALTEK_ARGB_HID_ID_DATA_CH4 0x5B
#define REALTEK_ARGB_HID_ID_DATA_CH5 0x5C
enum REALTEK_ARGB_LED_CTL
{
REALTEK_ARGB_LED_CTL_FW,
REALTEK_ARGB_LED_CTL_APP,
REALTEK_ARGB_LED_CTL_LAMP,
};
enum REALTEK_ARGB_EFFECT_ID
{
REALTEK_ARGB_EFF_NULL,
REALTEK_ARGB_EFF_ALWAYS_ON,
REALTEK_ARGB_EFF_BLINK,
REALTEK_ARGB_EFF_BREATH,
REALTEK_ARGB_EFF_SPECTRUM,
REALTEK_ARGB_EFF_SCROLL,
REALTEK_ARGB_EFF_RAINBOW_SCROLL,
REALTEK_ARGB_EFF_RUNNING_WATER,
REALTEK_ARGB_EFF_SLIDING,
REALTEK_ARGB_EFF_NEWTON_CRADLE,
REALTEK_ARGB_EFF_METEOR,
REALTEK_ARGB_EFF_RAINBOW_SLIDING,
REALTEK_ARGB_EFF_RAINBOW_FADE_SLIDING,
REALTEK_ARGB_EFF_WIDE_SLIDING,
REALTEK_ARGB_EFF_DOT_MATRIX, // reserved: Not supported by OpenRGB
REALTEK_ARGB_EFF_DOT_MATRIX_BREATH, // reserved: Not supported by OpenRGB
REALTEK_ARGB_EFF_ZIGZAG,
REALTEK_ARGB_EFF_STARRY_NIGHT,
REALTEK_ARGB_EFF_STACK,
};
enum REALTEK_ARGB_SPEED
{
REALTEK_ARGB_SPEED_MIN = 1,
REALTEK_ARGB_SPEED_NORMAL = 50,
REALTEK_ARGB_SPEED_MAX = 100,
};
enum REALTEK_ARGB_CYCLE_MS
{
REALTEK_ARGB_CYCLE_MIN = 200,
REALTEK_ARGB_CYCLE_NORMAL = 2000,
REALTEK_ARGB_CYCLE_MAX = 10000,
};
enum REALTEK_ARGB_CUST_DEVNAME
{
REALTEK_ARGB_CUST_DEVNAME_NULL = 0x0,
REALTEK_ARGB_CUST_DEVNAME_MANU_UUID = 0x1,
};
struct RealtekARGBControllerSetEffParam
{
unsigned int speed;
unsigned short brightness;
unsigned char dir;
unsigned char random_color;
};
struct RealtekARGBControllerFWVersion
{
unsigned int fw_major_ver;
unsigned int fw_minor_ver;
unsigned int fw_extra_ver;
unsigned int fw_build_ver;
unsigned int fw_build_date;
};
class RealtekARGBController
{
public:
RealtekARGBController(hid_device* dev, hid_device_info* info);
~RealtekARGBController();
unsigned char get_support_openrgb();
std::string get_manu_name();
std::string get_product_name();
std::string get_sn();
std::string get_dev_loc();
std::string get_fw_ver();
std::string get_ic_uuid();
std::string get_dev_name();
int get_fix_grps();
bool get_zone_enable(int grp_num);
int get_argb_num(int grp_num);
int get_argb_brightness(int grp_num);
int set_argb_brightness(int grp_num, unsigned short bright);
int set_argb_direct(int grp_num, std::vector<RGBColor> color_buf, unsigned short brightness);
int set_argb_effect(int grp_num, uint8_t mode, std::vector<RGBColor> color_buf, struct RealtekARGBControllerSetEffParam* param);
int set_argb_num(int grp_num, unsigned short new_num);
int device_reboot();
void device_rescan_trigger();
private:
hid_device* hdev;
std::mutex my_mutex;
unsigned char argbctl_hdr[REALTEK_ARGB_CTL_HDR_SIZE] = {0};
unsigned char* argbctl_data[REALTEK_ARGB_NUM_ARGB_GRP] = {0};
hid_device_info* hidinfo = NULL;
int appctl[REALTEK_ARGB_NUM_ARGB_GRP] = {REALTEK_ARGB_LED_CTL_FW};
unsigned short prev_bright[REALTEK_ARGB_NUM_ARGB_GRP] = {0};
std::atomic<bool> keepalive_thread_run;
std::thread* keepalive_thread;
unsigned char custled[16] = {0};
void KeepaliveThreadFunction();
int usb_hid_ioctl(unsigned char* usb_buf, unsigned char* data, int data_len,
unsigned int offset, unsigned char is_in);
int usb_hid_get_report(int data_len, int* id);
void device_init();
int set_write_unlock();
int set_appctl(unsigned char grp_num, unsigned char ctl_sts);
int set_argbctl_data(unsigned char* data, int data_len, int offset);
int get_custled(unsigned char* cust, unsigned int cust_len);
int get_argbctl_hdr();
int set_argbctl_hdr();
int get_argbctl_data();
int set_argbctl_data(unsigned char grp_num);
int set_eff_id(unsigned char grp_num, unsigned short effid);
int set_p_color(unsigned char grp_num, RGBColor color);
int set_s_color(unsigned char grp_num, RGBColor color);
int set_cycle(unsigned char grp_num, unsigned short cycle);
int set_ramp(unsigned char grp_num, unsigned short ramp);
int set_stable(unsigned char grp_num, unsigned short stable);
int set_subcmd(unsigned char grp_num, int subcmd);
int set_direct(unsigned char* color, int color_num, unsigned char grp_num);
int get_flash_argbctl_hdr(unsigned char* data);
int set_flash_argbctl_hdr(unsigned int offset, unsigned int data_len);
void device_rescan();
};
@@ -0,0 +1,52 @@
/*---------------------------------------------------------*\
| RealtekARGBControllerDetect.cpp |
| |
| Detector for Realtek USB ARGB ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "RGBController.h"
#include "RGBController_RealtekARGB.h"
/******************************************************************************************\
* *
* DetectRealtekARGBControllers *
* *
* Tests the USB address to see if an Realtek ARGB controller exists there *
* *
\******************************************************************************************/
void DetectRealtekARGBControllers(hid_device_info* info, const std::string& /*name*/)
{
RealtekARGBController* controller = NULL;
RGBController_RealtekARGB* rgb_controller = NULL;
hid_device* dev = hid_open_path(info->path);
if(dev)
{
controller = new RealtekARGBController(dev, info);
if(controller->get_support_openrgb())
{
rgb_controller = new RGBController_RealtekARGB(controller);
if(rgb_controller->type != DEVICE_TYPE_UNKNOWN)
{
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
else
{
delete rgb_controller;
}
}
else
{
delete controller;
}
}
return;
}
REGISTER_HID_DETECTOR_PU("RTL9209", DetectRealtekARGBControllers, REALTEK_ARGB_VID, REALTEK_ARGB_PID, REALTEK_ARGB_HID2SCSI_PG, REALTEK_ARGB_HID2SCSI_USAGE);
@@ -0,0 +1,303 @@
/*---------------------------------------------------------*\
| RGBController_RealtekBridge.cpp |
| |
| Controller for Realtek USB to SSD Bridge ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_RealtekBridge.h"
/**------------------------------------------------------------------*\
@name Realtek Bridge Device
@category Storage
@type USB
@save :x:
@direct :rotating_light:
@effects :white_check_mark:
@detectors RealtekBridgeControllerDetect
@comment
\*-------------------------------------------------------------------*/
RGBController_RealtekBridge::RGBController_RealtekBridge(RealtekBridgeController* controller_ptr)
{
controller = controller_ptr;
name = controller_ptr->get_product_name();
vendor = controller_ptr->get_manu_name();
location = controller_ptr->get_dev_loc();
serial = controller_ptr->get_sn();
version = controller_ptr->get_fw_ver();
description = vendor + "Storage Device";
type = DEVICE_TYPE_STORAGE;
SetupModes();
SetupZones();
}
RGBController_RealtekBridge::~RGBController_RealtekBridge()
{
delete controller;
}
void RGBController_RealtekBridge::SetupModes()
{
int brightness = controller->get_argb_brightness() >> 8;
mode Direct;
Direct.name = "Direct";
Direct.value = REALTEK_BRIDGE_LED_EFF_NONE;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.brightness_min = 0;
Direct.brightness_max = 255;
Direct.brightness = brightness;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = REALTEK_BRIDGE_LED_EFF_ALWAYS;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Static.colors_min = 1;
Static.colors_max = 1;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.colors.resize(1);
Static.brightness_min = 0;
Static.brightness_max = 255;
Static.brightness = brightness;
modes.push_back(Static);
mode Blink;
Blink.name = "Blink";
Blink.value = REALTEK_BRIDGE_LED_EFF_BLINK;
Blink.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Blink.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Blink.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Blink.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Blink.colors_min = 1;
Blink.colors_max = 1;
Blink.color_mode = MODE_COLORS_MODE_SPECIFIC;
Blink.colors.resize(1);
Blink.brightness_min = 0;
Blink.brightness_max = 255;
Blink.brightness = brightness;
modes.push_back(Blink);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = REALTEK_BRIDGE_LED_EFF_BREATHE;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Breathing.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Breathing.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Breathing.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
Breathing.colors.resize(1);
Breathing.brightness_min = 0;
Breathing.brightness_max = 255;
Breathing.brightness = brightness;
modes.push_back(Breathing);
mode Spectrum;
Spectrum.name = "Spectrum";
Spectrum.value = REALTEK_BRIDGE_LED_EFF_SPECTRUM;
Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Spectrum.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Spectrum.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Spectrum.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Spectrum.color_mode = MODE_COLORS_NONE;
Spectrum.brightness_min = 0;
Spectrum.brightness_max = 255;
Spectrum.brightness = brightness;
modes.push_back(Spectrum);
mode Scroll;
Scroll.name = "Scroll";
Scroll.value = REALTEK_BRIDGE_LED_EFF_SCROLL;
Scroll.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Scroll.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Scroll.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Scroll.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Scroll.colors_min = 1;
Scroll.colors_max = 1;
Scroll.color_mode = MODE_COLORS_MODE_SPECIFIC;
Scroll.colors.resize(1);
Scroll.brightness_min = 0;
Scroll.brightness_max = 255;
Scroll.brightness = brightness;
modes.push_back(Scroll);
mode RainbowScroll;
RainbowScroll.name = "Rainbow Scroll";
RainbowScroll.value = REALTEK_BRIDGE_LED_EFF_RAINBOW_SCROLL;
RainbowScroll.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
RainbowScroll.speed_min = REALTEK_BRIDGE_SPEED_MIN;
RainbowScroll.speed_max = REALTEK_BRIDGE_SPEED_MAX;
RainbowScroll.speed = REALTEK_BRIDGE_SPEED_NORMAL;
RainbowScroll.color_mode = MODE_COLORS_NONE;
RainbowScroll.brightness_min = 0;
RainbowScroll.brightness_max = 255;
RainbowScroll.brightness = brightness;
modes.push_back(RainbowScroll);
mode RunningWater;
RunningWater.name = "Running Water";
RunningWater.value = REALTEK_BRIDGE_LED_EFF_RUNNING_WATER;
RunningWater.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
RunningWater.speed_min = REALTEK_BRIDGE_SPEED_MIN;
RunningWater.speed_max = REALTEK_BRIDGE_SPEED_MAX;
RunningWater.speed = REALTEK_BRIDGE_SPEED_NORMAL;
RunningWater.colors_min = 1;
RunningWater.colors_max = 1;
RunningWater.color_mode = MODE_COLORS_MODE_SPECIFIC;
RunningWater.colors.resize(1);
RunningWater.brightness_min = 0;
RunningWater.brightness_max = 255;
RunningWater.brightness = brightness;
modes.push_back(RunningWater);
mode Sliding;
Sliding.name = "Sliding";
Sliding.value = REALTEK_BRIDGE_LED_EFF_SLIDING;
Sliding.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Sliding.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Sliding.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Sliding.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Sliding.color_mode = MODE_COLORS_NONE;
Sliding.brightness_min = 0;
Sliding.brightness_max = 255;
Sliding.brightness = brightness;
modes.push_back(Sliding);
mode NewtonCradle;
NewtonCradle.name = "Newton Cradle";
NewtonCradle.value = REALTEK_BRIDGE_LED_EFF_NEWTON_CRADLE;
NewtonCradle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
NewtonCradle.speed_min = REALTEK_BRIDGE_SPEED_MIN;
NewtonCradle.speed_max = REALTEK_BRIDGE_SPEED_MAX;
NewtonCradle.speed = REALTEK_BRIDGE_SPEED_NORMAL;
NewtonCradle.color_mode = MODE_COLORS_NONE;
NewtonCradle.brightness_min = 0;
NewtonCradle.brightness_max = 255;
NewtonCradle.brightness = brightness;
modes.push_back(NewtonCradle);
mode Meteor;
Meteor.name = "Meteor";
Meteor.value = REALTEK_BRIDGE_LED_EFF_METEOR;
Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Meteor.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Meteor.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Meteor.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Meteor.colors_min = 1;
Meteor.colors_max = 1;
Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC;
Meteor.colors.resize(1);
Meteor.brightness_min = 0;
Meteor.brightness_max = 255;
Meteor.brightness = brightness;
modes.push_back(Meteor);
}
void RGBController_RealtekBridge::SetupZones()
{
zone argb_zone;
argb_zone.name = "strip";
argb_zone.type = ZONE_TYPE_LINEAR;
argb_zone.leds_min = controller->get_argb_num();
argb_zone.leds_max = controller->get_argb_num();
argb_zone.leds_count = controller->get_argb_num();
argb_zone.matrix_map = NULL;
zones.push_back(argb_zone);
for(unsigned int led_idx = 0; led_idx < argb_zone.leds_count; led_idx++)
{
led StripLED;
StripLED.name = "led ";
StripLED.name.append(std::to_string(led_idx + 1));
leds.push_back(StripLED);
}
SetupColors();
}
void RGBController_RealtekBridge::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_RealtekBridge::DeviceUpdateLEDs()
{
UpdateZoneLEDs(0);
}
void RGBController_RealtekBridge::UpdateZoneLEDs(int /*zone*/)
{
unsigned short brightness = 0xFF;
mode& curr_mode = modes[active_mode];
if(curr_mode.color_mode == MODE_COLORS_PER_LED &&
curr_mode.value == REALTEK_BRIDGE_LED_EFF_NONE) //direct mode
{
if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS)
{
brightness = curr_mode.brightness;
}
controller->set_argb_direct(colors, brightness);
}
else
{
UpdateSingleLED(0);
}
}
void RGBController_RealtekBridge::UpdateSingleLED(int /*led*/)
{
unsigned char speed = REALTEK_BRIDGE_SPEED_NORMAL;
unsigned char dir = 0;
unsigned short brightness = 0xFF;
mode& curr_mode = modes[active_mode];
std::vector<RGBColor> rtk_colors = curr_mode.colors;
if(curr_mode.flags & MODE_FLAG_HAS_SPEED)
{
speed = curr_mode.speed;
}
if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS)
{
brightness = curr_mode.brightness;
}
if(curr_mode.flags & MODE_FLAG_HAS_DIRECTION_LR)
{
if(curr_mode.direction == MODE_DIRECTION_RIGHT)
{
dir = 1;
}
}
if(curr_mode.color_mode == MODE_COLORS_PER_LED)
{
rtk_colors = colors;
}
else if(curr_mode.color_mode == MODE_COLORS_NONE)
{
rtk_colors.clear();
}
controller->set_argb_effect(curr_mode.value, rtk_colors, speed, brightness);
}
void RGBController_RealtekBridge::DeviceUpdateMode()
{
if(modes[active_mode].value != REALTEK_BRIDGE_LED_EFF_NONE)
{
DeviceUpdateLEDs();
}
}
@@ -0,0 +1,34 @@
/*---------------------------------------------------------*\
| RGBController_RealtekBridge.h |
| |
| Controller for Realtek USB to SSD Bridge ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "RealtekBridgeController.h"
class RGBController_RealtekBridge : public RGBController
{
public:
RGBController_RealtekBridge(RealtekBridgeController* controller_ptr);
~RGBController_RealtekBridge();
void SetupModes();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
RealtekBridgeController* controller;
};
@@ -0,0 +1,802 @@
/*---------------------------------------------------------*\
| RealtekBridgeController.cpp |
| |
| Controller for Realtek USB to SSD Bridge ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RealtekBridgeController.h"
#include "hsv.h"
#include <StringUtils.h>
#include <MathUtils.h>
static const unsigned char hid_set_packet[] =
{
0x56, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xE3,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char hid_get_packet[] =
{
0x56, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x10, 0xE2,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char hid_end_packet[] =
{
0x58, 0x53, 0x42, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
RealtekBridgeController::RealtekBridgeController(hid_device* dev, hid_device_info* info)
{
hdev = dev;
hidinfo = info;
brctl_data = (unsigned char*)calloc(REALTEK_BRIDGE_CTL_DATA_SIZE, 1);
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
set_write_unlock();
get_brctl_hdr();
get_brctl_data();
set_appctl(true);
}
RealtekBridgeController::~RealtekBridgeController()
{
if(hdev)
{
set_appctl(false);
hid_close(hdev);
hdev = NULL;
}
memset(brctl_hdr, 0, REALTEK_BRIDGE_CTL_HDR_SIZE);
free(brctl_data);
}
int RealtekBridgeController::usb_hid_ioctl(unsigned char* usb_buf, unsigned char* data, int data_len,
unsigned int offset, unsigned char is_in)
{
int id;
int ret = 0;
int buf_len = usb_hid_get_report(data_len, &id);
if(!is_in && data_len)
{
usb_buf[0x04] = data[0];
}
memcpy(&usb_buf[0x08], &data_len, sizeof(data_len));
memcpy(&usb_buf[0x1B], &data_len, sizeof(data_len));
ret = hid_send_feature_report(hdev, usb_buf, sizeof(hid_get_packet));
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, data, data_len);
usb_buf[0x00] = id;
if(is_in)
{
ret = hid_get_feature_report(hdev, usb_buf, buf_len);
memcpy(data, usb_buf + offset, data_len);
}
else
{
ret = hid_send_feature_report(hdev, usb_buf, buf_len);
}
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_end_packet, sizeof(hid_end_packet));
ret = hid_get_feature_report(hdev, usb_buf, sizeof(hid_end_packet));
free(usb_buf);
return (ret > 0) ? 0 : ret;
}
int RealtekBridgeController::usb_hid_get_report(int data_len, int* id)
{
int retlen = 0;
if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH2)
{
*id = REALTEK_BRIDGE_HID_ID_DATA_CH2;
retlen = REALTEK_BRIDGE_HID_DATALEN_CH2;
}
else if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH3)
{
*id = REALTEK_BRIDGE_HID_ID_DATA_CH3;
retlen = REALTEK_BRIDGE_HID_DATALEN_CH3;
}
else if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH4)
{
*id = REALTEK_BRIDGE_HID_ID_DATA_CH4;
retlen = REALTEK_BRIDGE_HID_DATALEN_CH4;
}
else if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH5)
{
*id = REALTEK_BRIDGE_HID_ID_DATA_CH5;
retlen = REALTEK_BRIDGE_HID_DATALEN_CH5;
}
else
{
*id = REALTEK_BRIDGE_HID_ID_DATA_CH1;
retlen = REALTEK_BRIDGE_HID_DATALEN_CH1;
}
return retlen;
}
int RealtekBridgeController::set_write_unlock()
{
int ret = 0;
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
int data_len = 96;
unsigned int addr = 0xAC004000;
unsigned char* data = (unsigned char*)calloc(data_len, 1);
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0x92;
memcpy(&usb_buf[0x17], &addr, sizeof(addr));
ret = usb_hid_ioctl(usb_buf, data, data_len, 0, true);
free(data);
return ret;
}
int RealtekBridgeController::get_brctl_hdr()
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xCC;
usb_buf[0x14] = 0x02;
return usb_hid_ioctl(usb_buf, brctl_hdr, REALTEK_BRIDGE_CTL_HDR_SIZE, 0, true);
}
int RealtekBridgeController::set_brctl_hdr()
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x02;
return usb_hid_ioctl(usb_buf, brctl_hdr, REALTEK_BRIDGE_CTL_HDR_SIZE, 0, false);
}
int RealtekBridgeController::get_brctl_data()
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
int offset = REALTEK_BRIDGE_CTL_HDR_SIZE;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xCC;
usb_buf[0x14] = 0x02;
memcpy(&usb_buf[0x17], &offset, sizeof(offset));
return usb_hid_ioctl(usb_buf, brctl_data, REALTEK_BRIDGE_CTL_DATA_SIZE, 0, true);
}
int RealtekBridgeController::set_brctl_data()
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
int offset = REALTEK_BRIDGE_CTL_HDR_SIZE;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x02;
memcpy(&usb_buf[0x17], &offset, sizeof(offset));
return usb_hid_ioctl(usb_buf, brctl_data, REALTEK_BRIDGE_CTL_DATA_SIZE, 0, false);
}
unsigned char RealtekBridgeController::get_support_openrgb()
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
unsigned char is_support = 0;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xCC;
usb_buf[0x14] = 0x04;
usb_buf[0x15] = REALTEK_BRIDGE_SYNC_METHOD_OPENRGB;
if(usb_hid_ioctl(usb_buf, &is_support, sizeof(is_support), 0, true))
{
is_support = 0;
}
return is_support;
}
std::string RealtekBridgeController::get_manu_name()
{
return StringUtils::wchar_to_char(hidinfo->manufacturer_string);
}
std::string RealtekBridgeController::get_product_name()
{
return StringUtils::wchar_to_char(hidinfo->product_string);
}
std::string RealtekBridgeController::get_sn()
{
return StringUtils::wchar_to_char(hidinfo->serial_number);
}
std::string RealtekBridgeController::get_dev_loc()
{
return hidinfo->path;
}
std::string RealtekBridgeController::get_fw_ver()
{
struct RealtekBridgeControllerFWVersion fw_ver;
std::string ver = "";
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xA5;
if(!usb_hid_ioctl(usb_buf, (unsigned char*)&fw_ver, sizeof(fw_ver), 0, true))
{
ver += std::to_string(fw_ver.fw_major_ver) + "." +
std::to_string(fw_ver.fw_minor_ver) + "." +
std::to_string(fw_ver.fw_extra_ver) + "." +
std::to_string(fw_ver.fw_build_date);
}
return ver;
}
int RealtekBridgeController::get_argb_num()
{
int num_rgb = 0;
memcpy(&num_rgb, &brctl_hdr[24], sizeof(num_rgb));
return num_rgb;
}
int RealtekBridgeController::get_argb_brightness()
{
int bright = 0;
memcpy(&bright, &brctl_hdr[28], 2);
return bright;
}
int RealtekBridgeController::set_argb_brightness(unsigned short bright)
{
memcpy(&brctl_hdr[28], &bright, sizeof(bright));
set_brctl_hdr();
return 0;
}
int RealtekBridgeController::set_appctl(unsigned char ctl_sts)
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x01;
usb_buf[0x15] = ctl_sts;
return usb_hid_ioctl(usb_buf, &ctl_sts, sizeof(ctl_sts), 0, false);
}
int RealtekBridgeController::set_direct(unsigned char* color, int color_num)
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
int data_len = color_num * REALTEK_BRIDGE_COLOR_DEPTH;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x03;
usb_buf[0x15] = REALTEK_BRIDGE_SYNC_METHOD_OPENRGB;
memcpy(&usb_buf[0x17], &color_num, sizeof(color_num));
return usb_hid_ioctl(usb_buf, color, data_len, 0, false);
}
int RealtekBridgeController::eff_set_always_on(RGBColor rgb)
{
int i;
int j;
int row = 1;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 1;
brctl_data[i * single_size + 1] = row;
for(j = 0; j < row; j++)
{
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_blink(RGBColor rgb, int cycle)
{
int i;
int j;
int row = 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 2;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 1;
for(j = 0; j < row; j++)
{
if((j & 0x1) == 0)
{
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_breathe(RGBColor rgb, int cycle)
{
int i;
int j;
int row = 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 4;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 2;
for(j = 0; j < row; j++)
{
if((j & 0x1) == 0)
{
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_spectrum(int cycle)
{
int i;
int j;
int row = 6;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
static const RGBColor rainbow_table[6] = {0x0000FF, 0x0050FF, 0x0080FF, 0x00FF00, 0xFF0000, 0xFF0080};
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 4;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 3;
for(j = 0; j < row; j++)
{
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rainbow_table[j]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rainbow_table[j]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rainbow_table[j]);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_scroll(RGBColor rgb, int cycle)
{
int i;
int j;
int start_idx = -1;
int row = get_argb_num() * 2 - 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 2;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 4;
if(start_idx == -1)
{
start_idx = i;
}
j = i - start_idx;
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
if(i != start_idx)
{
j = row - (i - start_idx);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_rainbow_scroll(int cycle)
{
int i;
int j;
int start_idx = -1;
int row = get_argb_num() * 2 - 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
RGBColor slide_table[REALTEK_BRIDGE_MAX_ARGB_NUM] = {0};
hsv_t hsv_color;
hsv_color.saturation = 255;
hsv_color.value = 255;
for(i = 0; i < get_argb_num(); i++)
{
hsv_color.hue = (i % get_argb_num()) * (360 / get_argb_num());
slide_table[i] = hsv2rgb(&hsv_color);
}
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 2;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 0x85;
if(start_idx == -1)
{
start_idx = i;
}
j = i - start_idx;
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(slide_table[i]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(slide_table[i]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(slide_table[i]);
if(i != start_idx)
{
j = row - (i - start_idx);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(slide_table[i]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(slide_table[i]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(slide_table[i]);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_running_water(RGBColor rgb, int cycle)
{
int i;
int j;
int start_idx = -1;
int row = get_argb_num() * 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 2;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 5;
if(start_idx == -1)
{
start_idx = i;
}
for(j = i - start_idx; j < row / 2; j++)
{
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_sliding(int cycle)
{
int i;
int j;
int start_idx = -1;
int row = get_argb_num();
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
RGBColor slide_table[REALTEK_BRIDGE_MAX_ARGB_NUM] = {0};
RGBColor rgb = 0;
hsv_t hsv_color;
hsv_color.saturation = 255;
hsv_color.value = 255;
for(i = 0; i < get_argb_num(); i++)
{
hsv_color.hue = (i % get_argb_num()) * (360 / get_argb_num());
slide_table[i] = hsv2rgb(&hsv_color);
}
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 4;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 0x83;
if(start_idx == -1)
{
start_idx = i;
}
for(j = 0; j < row; j++)
{
rgb = slide_table[(i - start_idx + j) % row];
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_newton_cradle(int cycle)
{
int i;
int j;
int start_idx = -1;
int row;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
RGBColor rgb = 0;
static const int const_ball_num = 3;
static const RGBColor ball_color[] = {0x0080FF, 0x0000FF, 0x00FF00, 0xFF0000};
static const int mid = get_argb_num() >> 1;
if(get_argb_num() <= const_ball_num)
{
return 0;
}
row = (get_argb_num() - const_ball_num) * 2;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 2;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 0x84;
if(start_idx == -1)
{
start_idx = i;
}
for(j = 0; j < row; j++)
{
rgb = 0;
if(i - start_idx > mid - 2 &&
i - start_idx < mid + 2)
{
if(j < mid - 1 || j > row - mid)
{
if(i - start_idx == mid - 1)
{
rgb = ball_color[1];
}
else if(i - start_idx == mid)
{
rgb = ball_color[2];
}
else
{
rgb = ball_color[3];
}
}
else
{
if(i - start_idx == mid - 1)
{
rgb = ball_color[0];
}
else if(i - start_idx == mid)
{
rgb = ball_color[1];
}
else
{
rgb = ball_color[2];
}
}
}
else if(i - start_idx < mid)
{
if(i - start_idx == j || i - start_idx + j == row - 1)
{
rgb = ball_color[0];
}
}
else if(i - start_idx > mid + 1)
{
if(i - start_idx - j == const_ball_num ||
i - start_idx + j == row - 1 + const_ball_num)
{
rgb = ball_color[3];
}
}
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_meteor(RGBColor rgb, int cycle)
{
int i;
int j;
int row = 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
int stable_ms = cycle / row;
int latency = stable_ms / get_argb_num();
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 4;
brctl_data[i * single_size + 1] = row;
brctl_data[i * single_size + 6] = stable_ms / 100;
brctl_data[i * single_size + 7] = stable_ms / 100;
brctl_data[i * single_size + 8] = 6;
memcpy(&brctl_data[i * single_size + 9], &latency, 2);
j = 0;
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
return set_brctl_data();
}
int RealtekBridgeController::set_argb_direct(std::vector<RGBColor> color_buf, unsigned short brightness)
{
int ret = -1;
size_t color_num = color_buf.size();
size_t buf_len = color_num * REALTEK_BRIDGE_COLOR_DEPTH;
static unsigned short prev_bright = 0xFFFF;
unsigned char* buf;
if(color_num <= 0)
{
goto exit;
}
ret = set_appctl(true);
if(ret)
{
goto exit;
}
if(prev_bright != brightness)
{
prev_bright = brightness;
ret = set_argb_brightness(brightness << 8);
if(ret)
{
goto exit;
}
}
buf = (unsigned char*)malloc(buf_len);
memset(buf, 0, buf_len);
for(int i = 0; i < (int)color_num; i++)
{
buf[i * REALTEK_BRIDGE_COLOR_DEPTH + 0] = RGBGetRValue(color_buf[i]);
buf[i * REALTEK_BRIDGE_COLOR_DEPTH + 1] = RGBGetGValue(color_buf[i]);
buf[i * REALTEK_BRIDGE_COLOR_DEPTH + 2] = RGBGetBValue(color_buf[i]);
}
ret = set_direct(buf, (int)color_num);
free(buf);
if(ret)
{
goto exit;
}
exit:
return ret;
}
int RealtekBridgeController::set_argb_effect(unsigned char mode, std::vector<RGBColor> color_buf, int speed, unsigned short brightness)
{
int ret = -1;
int cycle = MathUtils::IntInterpolate(REALTEK_BRIDGE_CYCLE_MAX, REALTEK_BRIDGE_CYCLE_MIN, 0, REALTEK_BRIDGE_SPEED_MAX, speed);
RGBColor rgb = 0;
static unsigned short prev_bright = 0xFFFF;
if(color_buf.size() >= 1)
{
rgb = color_buf[0];
}
if(prev_bright != brightness)
{
prev_bright = brightness;
ret = set_argb_brightness(brightness << 8);
if(ret)
{
goto exit;
}
}
switch(mode)
{
case REALTEK_BRIDGE_LED_EFF_ALWAYS:
ret = eff_set_always_on(rgb);
break;
case REALTEK_BRIDGE_LED_EFF_BLINK:
ret = eff_set_blink(rgb, cycle);
break;
case REALTEK_BRIDGE_LED_EFF_BREATHE:
ret = eff_set_breathe(rgb, cycle);
break;
case REALTEK_BRIDGE_LED_EFF_SPECTRUM:
ret = eff_set_spectrum(cycle);
break;
case REALTEK_BRIDGE_LED_EFF_SCROLL:
ret = eff_set_scroll(rgb, cycle);
break;
case REALTEK_BRIDGE_LED_EFF_RAINBOW_SCROLL:
ret = eff_set_rainbow_scroll(cycle);
break;
case REALTEK_BRIDGE_LED_EFF_RUNNING_WATER:
ret = eff_set_running_water(rgb, cycle);
break;
case REALTEK_BRIDGE_LED_EFF_SLIDING:
ret = eff_set_sliding(cycle);
break;
case REALTEK_BRIDGE_LED_EFF_NEWTON_CRADLE:
ret = eff_set_newton_cradle(cycle);
break;
case REALTEK_BRIDGE_LED_EFF_METEOR:
ret = eff_set_meteor(rgb, cycle);
break;
default:
break;
}
if(!ret)
{
ret = set_appctl(false);
}
exit:
return ret;
}
@@ -0,0 +1,126 @@
/*---------------------------------------------------------*\
| RealtekBridgeController.h |
| |
| Controller for Realtek USB to SSD Bridge ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <cmath>
#include <cstring>
#include <vector>
#include <hidapi.h>
#include "RGBController.h"
#define REALTEK_BRIDGE_SYNC_METHOD_OPENRGB 6
#define REALTEK_BRIDGE_COLOR_DEPTH 3
#define REALTEK_BRIDGE_CTL_HDR_SIZE 64
#define REALTEK_BRIDGE_CTL_DATA_SIZE 3220
#define REALTEK_BRIDGE_MAX_ARGB_NUM 20
#define REALTEK_BRIDGE_MAX_APPCTL_ROW 43
#define REALTEK_BRIDGE_HID_DATALEN_CH1 4096
#define REALTEK_BRIDGE_HID_DATALEN_CH2 64
#define REALTEK_BRIDGE_HID_DATALEN_CH3 512
#define REALTEK_BRIDGE_HID_DATALEN_CH4 1200
#define REALTEK_BRIDGE_HID_DATALEN_CH5 2048
#define REALTEK_BRIDGE_HID_ID_DATA_CH1 0x57
#define REALTEK_BRIDGE_HID_ID_DATA_CH2 0x59
#define REALTEK_BRIDGE_HID_ID_DATA_CH3 0x5A
#define REALTEK_BRIDGE_HID_ID_DATA_CH4 0x5B
#define REALTEK_BRIDGE_HID_ID_DATA_CH5 0x5C
enum REALTEK_BRIDGE_LED_EFF
{
REALTEK_BRIDGE_LED_EFF_NONE,
REALTEK_BRIDGE_LED_EFF_ALWAYS,
REALTEK_BRIDGE_LED_EFF_BLINK,
REALTEK_BRIDGE_LED_EFF_BREATHE,
REALTEK_BRIDGE_LED_EFF_SPECTRUM,
REALTEK_BRIDGE_LED_EFF_SCROLL,
REALTEK_BRIDGE_LED_EFF_RAINBOW_SCROLL,
REALTEK_BRIDGE_LED_EFF_RUNNING_WATER,
REALTEK_BRIDGE_LED_EFF_SLIDING,
REALTEK_BRIDGE_LED_EFF_NEWTON_CRADLE,
REALTEK_BRIDGE_LED_EFF_METEOR,
REALTEK_BRIDGE_NUMBER_OF_LED_EFF_MODE,
};
enum REALTEK_BRIDGE_SPEED
{
REALTEK_BRIDGE_SPEED_MIN = 1,
REALTEK_BRIDGE_SPEED_NORMAL = 50,
REALTEK_BRIDGE_SPEED_MAX = 100,
};
enum REALTEK_BRIDGE_CYCLE_MS
{
REALTEK_BRIDGE_CYCLE_MIN = 200,
REALTEK_BRIDGE_CYCLE_NORMAL = 2000,
REALTEK_BRIDGE_CYCLE_MAX = 10000,
};
struct RealtekBridgeControllerFWVersion
{
unsigned int fw_major_ver;
unsigned int fw_minor_ver;
unsigned int fw_extra_ver;
unsigned int fw_build_ver;
unsigned int fw_build_date;
};
class RealtekBridgeController
{
public:
RealtekBridgeController(hid_device* dev, hid_device_info* info);
~RealtekBridgeController();
unsigned char get_support_openrgb();
std::string get_manu_name();
std::string get_product_name();
std::string get_sn();
std::string get_dev_loc();
std::string get_fw_ver();
int get_argb_num();
int get_argb_brightness();
int set_argb_brightness(unsigned short bright);
int set_argb_direct(std::vector<RGBColor> color_buf, unsigned short brightness);
int set_argb_effect(unsigned char mode, std::vector<RGBColor> color_buf, int speed, unsigned short brightness);
private:
hid_device* hdev;
hid_device_info* hidinfo = NULL;
unsigned char brctl_hdr[REALTEK_BRIDGE_CTL_HDR_SIZE] = {0};
unsigned char* brctl_data = NULL;
int usb_hid_ioctl(unsigned char* usb_buf, unsigned char* data, int data_len,
unsigned int offset, unsigned char is_in);
int usb_hid_get_report(int data_len, int* id);
int set_write_unlock();
int get_brctl_hdr();
int set_brctl_hdr();
int get_brctl_data();
int set_brctl_data();
int set_appctl(unsigned char ctl_sts);
int set_direct(unsigned char* color, int color_num);
int eff_set_always_on(RGBColor rgb);
int eff_set_blink(RGBColor rgb, int cycle);
int eff_set_breathe(RGBColor rgb, int cycle);
int eff_set_spectrum(int cycle);
int eff_set_scroll(RGBColor rgb, int cycle);
int eff_set_rainbow_scroll(int cycle);
int eff_set_running_water(RGBColor rgb, int cycle);
int eff_set_sliding(int cycle);
int eff_set_newton_cradle(int cycle);
int eff_set_meteor(RGBColor rgb, int cycle);
};
@@ -0,0 +1,61 @@
/*---------------------------------------------------------*\
| RealtekBridgeControllerDetect.cpp |
| |
| Controller for Realtek USB to SSD Bridge ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "RGBController_RealtekBridge.h"
#define REALTEK_BRIDGE_VID 0x0BDA
#define REALTEK_BRIDGE_PID0 0x9220
#define REALTEK_BRIDGE_PID1 0x9201
#define REALTEK_BRIDGE_PID2 0x9210
#define REALTEK_HID2SCSI_PG 0xFF00
#define REALTEK_HID2SCSI_USAGE 0x0001
/******************************************************************************************\
* *
* DetectRealtekBridgeControllers *
* *
* Tests the USB address to see if an Realtek Bridge controller exists there *
* *
\******************************************************************************************/
void DetectRealtekBridgeControllers(hid_device_info* info, const std::string& /*name*/)
{
RealtekBridgeController* controller = NULL;
RGBController_RealtekBridge* rgb_controller = NULL;
hid_device* dev = hid_open_path(info->path);
if(dev)
{
controller = new RealtekBridgeController(dev, info);
if(controller->get_support_openrgb())
{
rgb_controller = new RGBController_RealtekBridge(controller);
if(rgb_controller->type != DEVICE_TYPE_UNKNOWN)
{
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
else
{
delete rgb_controller;
}
}
else
{
delete controller;
}
}
return;
}
REGISTER_HID_DETECTOR_PU("RTL9220", DetectRealtekBridgeControllers, REALTEK_BRIDGE_VID, REALTEK_BRIDGE_PID0, REALTEK_HID2SCSI_PG, REALTEK_HID2SCSI_USAGE);
REGISTER_HID_DETECTOR_PU("RTL9201", DetectRealtekBridgeControllers, REALTEK_BRIDGE_VID, REALTEK_BRIDGE_PID1, REALTEK_HID2SCSI_PG, REALTEK_HID2SCSI_USAGE);
REGISTER_HID_DETECTOR_PU("RTL9210", DetectRealtekBridgeControllers, REALTEK_BRIDGE_VID, REALTEK_BRIDGE_PID2, REALTEK_HID2SCSI_PG, REALTEK_HID2SCSI_USAGE);
@@ -28,6 +28,7 @@
#define REDRAGON_M801_PID 0xFC58
#define REDRAGON_M810_PID 0xFA7E
#define REDRAGON_M987_PID 0xFC69
#define REDRAGON_M921_PID 0xFC40
/******************************************************************************************\
* *
@@ -62,3 +63,4 @@ REGISTER_HID_DETECTOR_IP("Redragon M808 Storm", DetectRedragonMice, RE
REGISTER_HID_DETECTOR_IP("Redragon M801 Sniper", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M801_PID, 2, REDRAGON_MOUSE_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("Redragon M810 Taipan", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M810_PID, 2, REDRAGON_MOUSE_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("Redragon M987 Reaping", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M987_PID, 2, REDRAGON_MOUSE_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("Redragon M921 Azzinoth", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M921_PID, 2, REDRAGON_MOUSE_USAGE_PAGE);
@@ -86,9 +86,14 @@ device_info RoccatVulcanKeyboardController::InitDeviceInfo()
buf[0] = report_id;
hid_get_feature_report(dev_ctrl, buf, packet_length);
char version[5];
snprintf(version, 5, "%d.%02d", buf[2] / 100, buf[2] % 100);
/*-------------------------------------------------------------*\
| buf[2] is version e.g. 103 means v1.03 |
\*-------------------------------------------------------------*/
dev_info.version_major = buf[2] / 100;
dev_info.version_minor = buf[2] % 100;
char version[5];
snprintf(version, 5, "%d.%02d", dev_info.version_major, dev_info.version_minor);
dev_info.version = version;
if(device_pid == ROCCAT_MAGMA_PID || device_pid == ROCCAT_MAGMA_MINI_PID)
@@ -116,6 +121,11 @@ device_info RoccatVulcanKeyboardController::GetDeviceInfo()
return dev_info;
}
bool RoccatVulcanKeyboardController::IsBigEndianDirectMode()
{
return (dev_info.version_major >= 1 && dev_info.version_minor >= 16);
}
void RoccatVulcanKeyboardController::EnableDirect(bool on_off_switch)
{
uint8_t* buf;
@@ -218,8 +228,16 @@ void RoccatVulcanKeyboardController::SendColors(std::vector<led_color> colors)
}
else
{
bufs[0][3] = packet_length & 0xFF;
bufs[0][4] = (packet_length >> 8) & 0xFF;
if(IsBigEndianDirectMode())
{
bufs[0][3] = (packet_length >> 8) & 0xFF;
bufs[0][4] = packet_length & 0xFF;
}
else
{
bufs[0][3] = packet_length & 0xFF;
bufs[0][4] = (packet_length >> 8) & 0xFF;
}
}
unsigned int data_length_packet = 64 - header_length_first;
@@ -61,6 +61,8 @@ enum
struct device_info
{
std::string version;
int version_major;
int version_minor;
int layout_type;
};
@@ -89,6 +91,7 @@ public:
void AwaitResponse(int ms);
void ClearResponses();
uint16_t device_pid;
private:
@@ -97,4 +100,6 @@ private:
device_info dev_info;
std::string location;
std::string name;
bool IsBigEndianDirectMode();
};
@@ -120,13 +120,13 @@
#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
#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
void DetectSteelSeriesAerox3(hid_device_info* info, const std::string& name)
{
@@ -516,10 +516,10 @@ REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL", Dete
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL 2023 Wired", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_PID, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL 2023 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_WL_PID_1, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL 2023 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_WL_PID_2, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL Gen 3 Wired", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_PID, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_I ("SteelSeries Apex M750", DetectSteelSeriesApexM, STEELSERIES_VID, STEELSERIES_APEX_M750_PID, 2 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex (OG)/Apex Fnatic", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_OG_PID, 0 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 350", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_350_PID, 0 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro 3", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO3_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL Gen 3", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_PID, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2, 3, 0xFFC0, 1);
@@ -46,7 +46,7 @@ std::string ThermaltakeRiingController::GetSerialString()
std::string ThermaltakeRiingController::GetFirmwareVersion()
{
unsigned char usb_buf[64];
unsigned char usb_buf[65];
/*-----------------------------------------------------*\
| Zero out buffer |
@@ -56,14 +56,15 @@ std::string ThermaltakeRiingController::GetFirmwareVersion()
/*-----------------------------------------------------*\
| Set up Get Firmware Version packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x33;
usb_buf[0x01] = 0x50;
usb_buf[0x00] = 0x00;
usb_buf[0x01] = 0x33;
usb_buf[0x02] = 0x50;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 64);
hid_read(dev, usb_buf, 64);
hid_write(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, 100);
std::string ret_str = std::to_string(usb_buf[2]) + "." + std::to_string(usb_buf[3]) + "." + std::to_string(usb_buf[4]);
@@ -117,7 +118,7 @@ void ThermaltakeRiingController::SendInit()
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, 100);
}
void ThermaltakeRiingController::SendRGB
@@ -154,5 +155,5 @@ void ThermaltakeRiingController::SendRGB
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, 100);
}
@@ -0,0 +1,353 @@
/*---------------------------------------------------------*\
| RGBController_ThrustmasterSol.cpp |
| |
| RGBController for Thrustmaster Sol series joysticks |
| |
| Ken Sanislo 02 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_ThrustmasterSol.h"
/**------------------------------------------------------------------*\
@name Thrustmaster Sol
@category Gamepad
@type USB
@save :white_check_mark:
@direct :white_check_mark:
@effects :x:
@detectors DetectThrustmasterSolControllers
@comment Thrustmaster Sol series joystick RGB LED control. Supports
Sol-R, Sol F16, and Sol F18 variants. Only Sol-R has been tested.
Uses vendor-specific USB interface 1 (not HID).
\*-------------------------------------------------------------------*/
#define NA 0xFFFFFFFF
static unsigned int logo_matrix_map[2][2] =
{
{ 0, 3 },
{ 1, 2 },
};
static unsigned int ring_matrix_map[3][5] =
{
{ NA, 7, 0, 1, NA },
{ 6, NA, NA, NA, 2 },
{ NA, 5, 4, 3, NA },
};
static unsigned int right_buttons_matrix_map[2][2] =
{
{ 0, 1 },
{ 2, 3 },
};
static unsigned int left_buttons_matrix_map[2][2] =
{
{ 0, 1 },
{ 2, 3 },
};
RGBController_ThrustmasterSol::RGBController_ThrustmasterSol(ThrustmasterSolController* controller_ptr)
{
controller = controller_ptr;
vendor = "Thrustmaster";
type = DEVICE_TYPE_GAMEPAD;
description = "Thrustmaster Sol Series Joystick";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
mode Direct;
Direct.name = "Direct";
Direct.value = 0;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
/*---------------------------------------------------------*\
| Read current EEPROM colors from device so the UI starts |
| with the actual hardware state rather than all-black |
\*---------------------------------------------------------*/
std::vector<unsigned int> hw_zones;
std::vector<RGBColor> hw_colors;
controller->ReadColors(hw_zones, hw_colors);
for(unsigned int i = 0; i < hw_zones.size(); i++)
{
for(unsigned int j = 0; j < leds.size(); j++)
{
if(leds[j].value == hw_zones[i])
{
colors[j] = hw_colors[i];
break;
}
}
}
}
RGBController_ThrustmasterSol::~RGBController_ThrustmasterSol()
{
delete controller;
}
void RGBController_ThrustmasterSol::SetupZones()
{
/*---------------------------------------------------------*\
| Thumbstick zone (grip, 1 LED) |
\*---------------------------------------------------------*/
zone thumbstick_zone;
thumbstick_zone.name = "Thumbstick";
thumbstick_zone.type = ZONE_TYPE_SINGLE;
thumbstick_zone.leds_min = 1;
thumbstick_zone.leds_max = 1;
thumbstick_zone.leds_count = 1;
thumbstick_zone.matrix_map = NULL;
zones.push_back(thumbstick_zone);
led thumbstick_led;
thumbstick_led.name = "Thumbstick";
thumbstick_led.value = THRUSTMASTER_SOL_GRIP_FLAG | 0x00;
leds.push_back(thumbstick_led);
/*---------------------------------------------------------*\
| TM Logo zone (3 LEDs) |
\*---------------------------------------------------------*/
zone logo_zone;
logo_zone.name = "TM Logo";
logo_zone.type = ZONE_TYPE_MATRIX;
logo_zone.leds_min = 4;
logo_zone.leds_max = 4;
logo_zone.leds_count = 4;
logo_zone.matrix_map = new matrix_map_type;
logo_zone.matrix_map->height = 2;
logo_zone.matrix_map->width = 2;
logo_zone.matrix_map->map = (unsigned int *)&logo_matrix_map;
zones.push_back(logo_zone);
led logo_top_left;
logo_top_left.name = "TM Logo Top Left";
logo_top_left.value = 0x01;
leds.push_back(logo_top_left);
led logo_bottom_left;
logo_bottom_left.name = "TM Logo Bottom Left";
logo_bottom_left.value = 0x02;
leds.push_back(logo_bottom_left);
led logo_bottom_right;
logo_bottom_right.name = "TM Logo Bottom Right";
logo_bottom_right.value = 0x03;
leds.push_back(logo_bottom_right);
led logo_top_right;
logo_top_right.name = "TM Logo Top Right";
logo_top_right.value = 0x00;
leds.push_back(logo_top_right);
/*---------------------------------------------------------*\
| Left Buttons zone (4 LEDs: buttons 5-8) |
\*---------------------------------------------------------*/
zone left_buttons_zone;
left_buttons_zone.name = "Left Buttons";
left_buttons_zone.type = ZONE_TYPE_MATRIX;
left_buttons_zone.leds_min = 4;
left_buttons_zone.leds_max = 4;
left_buttons_zone.leds_count = 4;
left_buttons_zone.matrix_map = new matrix_map_type;
left_buttons_zone.matrix_map->height = 2;
left_buttons_zone.matrix_map->width = 2;
left_buttons_zone.matrix_map->map = (unsigned int *)&left_buttons_matrix_map;
zones.push_back(left_buttons_zone);
led btn5;
btn5.name = "Button 5";
btn5.value = 0x11;
leds.push_back(btn5);
led btn6;
btn6.name = "Button 6";
btn6.value = 0x10;
leds.push_back(btn6);
led btn7;
btn7.name = "Button 7";
btn7.value = 0x12;
leds.push_back(btn7);
led btn8;
btn8.name = "Button 8";
btn8.value = 0x13;
leds.push_back(btn8);
/*---------------------------------------------------------*\
| Base Ring zone (8 LEDs, clockwise from top) |
\*---------------------------------------------------------*/
zone ring_zone;
ring_zone.name = "Base Ring";
ring_zone.type = ZONE_TYPE_MATRIX;
ring_zone.leds_min = 8;
ring_zone.leds_max = 8;
ring_zone.leds_count = 8;
ring_zone.matrix_map = new matrix_map_type;
ring_zone.matrix_map->height = 3;
ring_zone.matrix_map->width = 5;
ring_zone.matrix_map->map = (unsigned int *)&ring_matrix_map;
zones.push_back(ring_zone);
led ring_upper;
ring_upper.name = "Upper";
ring_upper.value = 0x04;
leds.push_back(ring_upper);
led ring_upper_right;
ring_upper_right.name = "Upper Right";
ring_upper_right.value = 0x05;
leds.push_back(ring_upper_right);
led ring_right;
ring_right.name = "Right";
ring_right.value = 0x06;
leds.push_back(ring_right);
led ring_bottom_right;
ring_bottom_right.name = "Bottom Right";
ring_bottom_right.value = 0x0B;
leds.push_back(ring_bottom_right);
led ring_bottom;
ring_bottom.name = "Bottom";
ring_bottom.value = 0x0C;
leds.push_back(ring_bottom);
led ring_bottom_left;
ring_bottom_left.name = "Bottom Left";
ring_bottom_left.value = 0x0D;
leds.push_back(ring_bottom_left);
led ring_left;
ring_left.name = "Left";
ring_left.value = 0x0E;
leds.push_back(ring_left);
led ring_upper_left;
ring_upper_left.name = "Upper Left";
ring_upper_left.value = 0x0F;
leds.push_back(ring_upper_left);
/*---------------------------------------------------------*\
| Right Buttons zone (4 LEDs: buttons 16-19) |
\*---------------------------------------------------------*/
zone right_buttons_zone;
right_buttons_zone.name = "Right Buttons";
right_buttons_zone.type = ZONE_TYPE_MATRIX;
right_buttons_zone.leds_min = 4;
right_buttons_zone.leds_max = 4;
right_buttons_zone.leds_count = 4;
right_buttons_zone.matrix_map = new matrix_map_type;
right_buttons_zone.matrix_map->height = 2;
right_buttons_zone.matrix_map->width = 2;
right_buttons_zone.matrix_map->map = (unsigned int *)&right_buttons_matrix_map;
zones.push_back(right_buttons_zone);
led btn17;
btn17.name = "Button 17";
btn17.value = 0x07;
leds.push_back(btn17);
led btn16;
btn16.name = "Button 16";
btn16.value = 0x08;
leds.push_back(btn16);
led btn19;
btn19.name = "Button 19";
btn19.value = 0x0A;
leds.push_back(btn19);
led btn18;
btn18.name = "Button 18";
btn18.value = 0x09;
leds.push_back(btn18);
SetupColors();
}
void RGBController_ThrustmasterSol::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_ThrustmasterSol::DeviceUpdateLEDs()
{
unsigned int led_zones[THRUSTMASTER_SOL_R_ZONE_COUNT];
RGBColor led_colors[THRUSTMASTER_SOL_R_ZONE_COUNT];
for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++)
{
led_zones[led_idx] = leds[led_idx].value;
led_colors[led_idx] = colors[led_idx];
}
controller->SetLEDColors(led_zones, led_colors, static_cast<unsigned int>(leds.size()));
}
void RGBController_ThrustmasterSol::UpdateZoneLEDs(int zone)
{
unsigned int start_idx = 0;
unsigned int zone_size = 0;
for(unsigned int z_idx = 0; z_idx < zones.size(); z_idx++)
{
if(z_idx == (unsigned int)zone)
{
zone_size = zones[z_idx].leds_count;
break;
}
start_idx += zones[z_idx].leds_count;
}
unsigned int led_zones[THRUSTMASTER_SOL_R_ZONE_COUNT];
RGBColor led_colors[THRUSTMASTER_SOL_R_ZONE_COUNT];
for(unsigned int led_idx = 0; led_idx < zone_size; led_idx++)
{
unsigned int current_idx = start_idx + led_idx;
led_zones[led_idx] = leds[current_idx].value;
led_colors[led_idx] = colors[current_idx];
}
controller->SetLEDColors(led_zones, led_colors, zone_size);
}
void RGBController_ThrustmasterSol::UpdateSingleLED(int led)
{
controller->SetLEDColor(leds[led].value, colors[led]);
}
void RGBController_ThrustmasterSol::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
void RGBController_ThrustmasterSol::DeviceSaveMode()
{
unsigned int led_zones[THRUSTMASTER_SOL_R_ZONE_COUNT];
RGBColor led_colors[THRUSTMASTER_SOL_R_ZONE_COUNT];
for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++)
{
led_zones[led_idx] = leds[led_idx].value;
led_colors[led_idx] = colors[led_idx];
}
controller->SaveColors(led_zones, led_colors, static_cast<unsigned int>(leds.size()));
}
@@ -0,0 +1,35 @@
/*---------------------------------------------------------*\
| RGBController_ThrustmasterSol.h |
| |
| RGBController for Thrustmaster Sol series joysticks |
| |
| Ken Sanislo 02 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "ThrustmasterSolController.h"
class RGBController_ThrustmasterSol : public RGBController
{
public:
RGBController_ThrustmasterSol(ThrustmasterSolController* controller_ptr);
~RGBController_ThrustmasterSol();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
ThrustmasterSolController* controller;
};
@@ -0,0 +1,315 @@
/*---------------------------------------------------------*\
| ThrustmasterSolController.cpp |
| |
| Driver for Thrustmaster Sol series joysticks |
| |
| Ken Sanislo 02 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include <thread>
#include <chrono>
#include "ThrustmasterSolController.h"
#include "StringUtils.h"
ThrustmasterSolController::ThrustmasterSolController(libusb_device_handle* dev_handle,
const char* path,
unsigned short pid)
{
dev = dev_handle;
this->pid = pid;
location = "USB: ";
location += path;
/*---------------------------------------------------------*\
| Get serial number string descriptor |
\*---------------------------------------------------------*/
libusb_device_descriptor desc;
libusb_get_device_descriptor(libusb_get_device(dev_handle), &desc);
if(desc.iSerialNumber != 0)
{
unsigned char serial_str[256];
int ret = libusb_get_string_descriptor_ascii(dev_handle,
desc.iSerialNumber,
serial_str,
sizeof(serial_str));
if(ret > 0)
{
serial = std::string(reinterpret_cast<char*>(serial_str), ret);
}
}
}
ThrustmasterSolController::~ThrustmasterSolController()
{
if(dev != nullptr)
{
libusb_release_interface(dev, THRUSTMASTER_SOL_INTERFACE);
libusb_close(dev);
}
}
std::string ThrustmasterSolController::GetDeviceLocation()
{
return(location);
}
std::string ThrustmasterSolController::GetSerialString()
{
return(serial);
}
unsigned short ThrustmasterSolController::GetPID()
{
return(pid);
}
void ThrustmasterSolController::SendPacket(unsigned char* packet, unsigned int size)
{
if(dev == nullptr)
{
return;
}
int actual_length = 0;
libusb_interrupt_transfer(dev,
THRUSTMASTER_SOL_ENDPOINT_OUT,
packet,
size,
&actual_length,
1000);
}
void ThrustmasterSolController::BuildAndSendPackets(unsigned int* zones,
RGBColor* colors,
unsigned int count,
bool persistent)
{
unsigned char persist_flag = persistent ? THRUSTMASTER_SOL_PERSISTENT
: THRUSTMASTER_SOL_VOLATILE;
/*---------------------------------------------------------*\
| Separate grip zones from base zones. Zone 0x00 exists on |
| both the base (logo LED) and the grip (thumbstick LED). |
| The GRIP_FLAG bit selects the grip report type. |
\*---------------------------------------------------------*/
unsigned char base_zones[THRUSTMASTER_SOL_R_ZONE_COUNT];
RGBColor base_colors[THRUSTMASTER_SOL_R_ZONE_COUNT];
unsigned int base_count = 0;
for(unsigned int i = 0; i < count; i++)
{
if(zones[i] & THRUSTMASTER_SOL_GRIP_FLAG)
{
/*-------------------------------------------------*\
| Send grip packet for thumbstick immediately |
\*-------------------------------------------------*/
unsigned char zone_id = zones[i] & 0xFF;
unsigned char packet[THRUSTMASTER_SOL_PACKET_SIZE];
memset(packet, 0x00, THRUSTMASTER_SOL_PACKET_SIZE);
packet[0] = THRUSTMASTER_SOL_REPORT_GRIP_LO;
packet[1] = THRUSTMASTER_SOL_REPORT_GRIP_HI;
packet[2] = persist_flag | 0x01;
packet[3] = THRUSTMASTER_SOL_MARKER;
packet[4] = zone_id;
packet[5] = RGBGetRValue(colors[i]);
packet[6] = RGBGetGValue(colors[i]);
packet[7] = RGBGetBValue(colors[i]);
SendPacket(packet, THRUSTMASTER_SOL_PACKET_SIZE);
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
else
{
base_zones[base_count] = zones[i] & 0xFF;
base_colors[base_count] = colors[i];
base_count++;
}
}
/*---------------------------------------------------------*\
| Send base zone packets, batching up to 15 entries each |
\*---------------------------------------------------------*/
for(unsigned int offset = 0; offset < base_count; offset += THRUSTMASTER_SOL_MAX_ENTRIES)
{
unsigned int entries = base_count - offset;
if(entries > THRUSTMASTER_SOL_MAX_ENTRIES)
{
entries = THRUSTMASTER_SOL_MAX_ENTRIES;
}
unsigned char packet[THRUSTMASTER_SOL_PACKET_SIZE];
memset(packet, 0x00, THRUSTMASTER_SOL_PACKET_SIZE);
packet[0] = THRUSTMASTER_SOL_REPORT_BASE_LO;
packet[1] = THRUSTMASTER_SOL_REPORT_BASE_HI;
packet[2] = persist_flag | (unsigned char)entries;
packet[3] = THRUSTMASTER_SOL_MARKER;
for(unsigned int j = 0; j < entries; j++)
{
unsigned int idx = offset + j;
packet[4 + j * 4] = base_zones[idx];
packet[4 + j * 4 + 1] = RGBGetRValue(base_colors[idx]);
packet[4 + j * 4 + 2] = RGBGetGValue(base_colors[idx]);
packet[4 + j * 4 + 3] = RGBGetBValue(base_colors[idx]);
}
SendPacket(packet, THRUSTMASTER_SOL_PACKET_SIZE);
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
}
void ThrustmasterSolController::SetLEDColor(unsigned int zone, RGBColor color)
{
BuildAndSendPackets(&zone, &color, 1, false);
}
void ThrustmasterSolController::SetLEDColors(unsigned int* zones,
RGBColor* colors,
unsigned int count)
{
BuildAndSendPackets(zones, colors, count, false);
}
void ThrustmasterSolController::SaveColors(unsigned int* zones,
RGBColor* colors,
unsigned int count)
{
BuildAndSendPackets(zones, colors, count, true);
}
void ThrustmasterSolController::ReadColors(std::vector<unsigned int>& zones,
std::vector<RGBColor>& colors)
{
if(dev == nullptr)
{
return;
}
zones.clear();
colors.clear();
/*---------------------------------------------------------*\
| Read base zones (report 0x0002). May require multiple |
| queries if more than 14 zones are returned per response. |
| Zone IDs are returned without the grip flag, so they |
| match the base LED values (including zone 0x00 = logo). |
\*---------------------------------------------------------*/
unsigned char start = 0;
for(int page = 0; page < 4; page++)
{
unsigned char query[THRUSTMASTER_SOL_PACKET_SIZE];
memset(query, 0x00, THRUSTMASTER_SOL_PACKET_SIZE);
query[0] = THRUSTMASTER_SOL_READ_BASE_LO;
query[1] = THRUSTMASTER_SOL_READ_BASE_HI;
query[2] = start;
SendPacket(query, THRUSTMASTER_SOL_PACKET_SIZE);
unsigned char resp[THRUSTMASTER_SOL_PACKET_SIZE];
int actual = 0;
int ret = libusb_interrupt_transfer(dev,
THRUSTMASTER_SOL_ENDPOINT_IN,
resp,
THRUSTMASTER_SOL_PACKET_SIZE,
&actual,
1000);
if(ret != LIBUSB_SUCCESS || actual < 4)
{
break;
}
unsigned int n = resp[2] & 0x0F;
if(n == 0)
{
break;
}
unsigned char last = start;
for(unsigned int i = 0; i < n; i++)
{
unsigned int off = 4 + i * 4;
if(off + 3 >= (unsigned int)actual)
{
break;
}
unsigned char zid = resp[off];
unsigned char r = resp[off + 1];
unsigned char g = resp[off + 2];
unsigned char b = resp[off + 3];
zones.push_back(zid);
colors.push_back(ToRGBColor(r, g, b));
last = zid;
}
start = last + 1;
if(n < 14 || start > 0x20)
{
break;
}
}
/*---------------------------------------------------------*\
| Read grip zones (report 0x8002). Grip zone IDs are |
| returned with the GRIP_FLAG set so they match the |
| thumbstick LED value. They are added as new entries, |
| not overwriting base entries (zone 0x00 exists on both). |
\*---------------------------------------------------------*/
unsigned char grip_query[THRUSTMASTER_SOL_PACKET_SIZE];
memset(grip_query, 0x00, THRUSTMASTER_SOL_PACKET_SIZE);
grip_query[0] = THRUSTMASTER_SOL_READ_GRIP_LO;
grip_query[1] = THRUSTMASTER_SOL_READ_GRIP_HI;
SendPacket(grip_query, THRUSTMASTER_SOL_PACKET_SIZE);
unsigned char grip_resp[THRUSTMASTER_SOL_PACKET_SIZE];
int grip_actual = 0;
int grip_ret = libusb_interrupt_transfer(dev,
THRUSTMASTER_SOL_ENDPOINT_IN,
grip_resp,
THRUSTMASTER_SOL_PACKET_SIZE,
&grip_actual,
1000);
if(grip_ret == LIBUSB_SUCCESS && grip_actual >= 8)
{
unsigned int grip_n = grip_resp[2] & 0x0F;
for(unsigned int i = 0; i < grip_n; i++)
{
unsigned int off = 4 + i * 4;
if(off + 3 >= (unsigned int)grip_actual)
{
break;
}
unsigned char zid = grip_resp[off];
unsigned char r = grip_resp[off + 1];
unsigned char g = grip_resp[off + 2];
unsigned char b = grip_resp[off + 3];
zones.push_back(THRUSTMASTER_SOL_GRIP_FLAG | zid);
colors.push_back(ToRGBColor(r, g, b));
}
}
}
@@ -0,0 +1,116 @@
/*---------------------------------------------------------*\
| ThrustmasterSolController.h |
| |
| Driver for Thrustmaster Sol series joysticks |
| |
| Ken Sanislo 02 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <vector>
#include "RGBController.h"
#ifdef _WIN32
#include "dependencies/libusb-1.0.27/include/libusb.h"
#else
#include <libusb.h>
#endif
/*-----------------------------------------------------*\
| Thrustmaster vendor ID |
\*-----------------------------------------------------*/
#define THRUSTMASTER_VID 0x044F
/*-----------------------------------------------------*\
| Thrustmaster Sol series product IDs |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_R_RIGHT_PID 0x0422
#define THRUSTMASTER_SOL_R_LEFT_PID 0x042A
#define THRUSTMASTER_SOL_F16_RIGHT_PID 0x0420
#define THRUSTMASTER_SOL_F18_RIGHT_PID 0x0421
#define THRUSTMASTER_SOL_F16_LEFT_PID 0x0428
#define THRUSTMASTER_SOL_F18_LEFT_PID 0x0429
/*-----------------------------------------------------*\
| Thrustmaster Sol USB constants |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_INTERFACE 1
#define THRUSTMASTER_SOL_ENDPOINT_OUT 0x02
#define THRUSTMASTER_SOL_ENDPOINT_IN 0x82
#define THRUSTMASTER_SOL_PACKET_SIZE 64
#define THRUSTMASTER_SOL_MAX_ENTRIES 15
/*-----------------------------------------------------*\
| Thrustmaster Sol report type identifiers |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_REPORT_BASE_LO 0x01
#define THRUSTMASTER_SOL_REPORT_BASE_HI 0x08
#define THRUSTMASTER_SOL_REPORT_GRIP_LO 0x01
#define THRUSTMASTER_SOL_REPORT_GRIP_HI 0x88
/*-----------------------------------------------------*\
| Thrustmaster Sol persistence flags |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_VOLATILE 0x00
#define THRUSTMASTER_SOL_PERSISTENT 0x80
/*-----------------------------------------------------*\
| Thrustmaster Sol read query report types |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_READ_BASE_LO 0x02
#define THRUSTMASTER_SOL_READ_BASE_HI 0x00
#define THRUSTMASTER_SOL_READ_GRIP_LO 0x02
#define THRUSTMASTER_SOL_READ_GRIP_HI 0x80
/*-----------------------------------------------------*\
| Thrustmaster Sol zone constants |
| |
| Zone 0x00 is shared: on the base it is a logo LED, |
| on the grip it is the thumbstick LED. The grip flag |
| (bit 8) selects the grip report type for zone 0x00. |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_ZONE_THUMBSTICK 0x00
#define THRUSTMASTER_SOL_GRIP_FLAG 0x100
#define THRUSTMASTER_SOL_MARKER 0xFF
/*-----------------------------------------------------*\
| Thrustmaster Sol zone count for Sol-R |
| 19 base zones + thumbstick + logo zone 0x00 = 21 |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_R_ZONE_COUNT 21
class ThrustmasterSolController
{
public:
ThrustmasterSolController(libusb_device_handle* dev_handle,
const char* path,
unsigned short pid);
~ThrustmasterSolController();
std::string GetDeviceLocation();
std::string GetSerialString();
unsigned short GetPID();
void SetLEDColor(unsigned int zone, RGBColor color);
void SetLEDColors(unsigned int* zones, RGBColor* colors,
unsigned int count);
void SaveColors(unsigned int* zones, RGBColor* colors,
unsigned int count);
void ReadColors(std::vector<unsigned int>& zones,
std::vector<RGBColor>& colors);
private:
libusb_device_handle* dev;
std::string location;
std::string serial;
unsigned short pid;
void SendPacket(unsigned char* packet, unsigned int size);
void BuildAndSendPackets(unsigned int* zones, RGBColor* colors,
unsigned int count, bool persistent);
};
@@ -0,0 +1,125 @@
/*---------------------------------------------------------*\
| ThrustmasterSolControllerDetect.cpp |
| |
| Detector for Thrustmaster Sol series joysticks |
| |
| Ken Sanislo 02 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "ThrustmasterSolController.h"
#include "RGBController_ThrustmasterSol.h"
typedef struct
{
unsigned short usb_vid;
unsigned short usb_pid;
const char* name;
} thrustmaster_sol_device;
#define THRUSTMASTER_SOL_NUM_DEVICES (sizeof(device_list) / sizeof(device_list[0]))
static const thrustmaster_sol_device device_list[] =
{
/*-----------------------------------------------------------------------------------------------------*\
| Sol-R variants (tested) |
\*-----------------------------------------------------------------------------------------------------*/
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_R_RIGHT_PID, "Thrustmaster Sol-R Right" },
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_R_LEFT_PID, "Thrustmaster Sol-R Left" },
/*-----------------------------------------------------------------------------------------------------*\
| Sol F16/F18 variants (untested, same base protocol) |
\*-----------------------------------------------------------------------------------------------------*/
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_F16_RIGHT_PID, "Thrustmaster Sol F16 Right" },
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_F18_RIGHT_PID, "Thrustmaster Sol F18 Right" },
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_F16_LEFT_PID, "Thrustmaster Sol F16 Left" },
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_F18_LEFT_PID, "Thrustmaster Sol F18 Left" },
};
/******************************************************************************************\
* *
* DetectThrustmasterSolControllers *
* *
* Detect Thrustmaster Sol series joysticks via libusb. *
* LED control is on USB interface 1 (vendor-specific class 255, not HID). *
* Interface 0 (joystick HID) is not touched. *
* *
\******************************************************************************************/
void DetectThrustmasterSolControllers()
{
libusb_init(NULL);
#ifdef _WIN32
libusb_set_option(NULL, LIBUSB_OPTION_USE_USBDK);
#endif
libusb_device** devs;
ssize_t num_devs = libusb_get_device_list(NULL, &devs);
if(num_devs <= 0)
{
return;
}
for(ssize_t i = 0; i < num_devs; i++)
{
libusb_device_descriptor desc;
if(libusb_get_device_descriptor(devs[i], &desc) != 0)
{
continue;
}
for(std::size_t d = 0; d < THRUSTMASTER_SOL_NUM_DEVICES; d++)
{
if(desc.idVendor == device_list[d].usb_vid &&
desc.idProduct == device_list[d].usb_pid)
{
libusb_device_handle* handle = NULL;
if(libusb_open(devs[i], &handle) != LIBUSB_SUCCESS)
{
continue;
}
libusb_set_auto_detach_kernel_driver(handle, 1);
if(libusb_claim_interface(handle, THRUSTMASTER_SOL_INTERFACE) != LIBUSB_SUCCESS)
{
libusb_close(handle);
continue;
}
uint8_t bus = libusb_get_bus_number(devs[i]);
uint8_t address = libusb_get_device_address(devs[i]);
char path[32];
snprintf(path, sizeof(path), "%d-%d", bus, address);
ThrustmasterSolController* controller = new ThrustmasterSolController(handle, path, desc.idProduct);
RGBController_ThrustmasterSol* rgb_controller = new RGBController_ThrustmasterSol(controller);
rgb_controller->name = device_list[d].name;
ResourceManager::get()->RegisterRGBController(rgb_controller);
break;
}
}
}
libusb_free_device_list(devs, 1);
}
REGISTER_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers);
/*---------------------------------------------------------------------------------------------------------*\
| Entries for dynamic UDEV rules |
| |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0420 ) |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0421 ) |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0422 ) |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0428 ) |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0429 ) |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x042A ) |
\*---------------------------------------------------------------------------------------------------------*/
@@ -187,50 +187,21 @@ static const char *led_names[] =
\*---------------------------------------------------------*/
RGBController_XPGSummoner::RGBController_XPGSummoner(XPGSummonerController *controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "XPG";
description = "XPG Summoner Keyboard Device";
location = controller->GetLocationString();
serial = controller->GetSerialString();
type = DEVICE_TYPE_KEYBOARD;
controller = controller_ptr;
name = controller->GetNameString();
vendor = "XPG";
description = "XPG Summoner Keyboard Device";
location = controller->GetLocationString();
serial = controller->GetSerialString();
type = DEVICE_TYPE_KEYBOARD;
mode Direct;
Direct.name = "Direct";
Direct.value = XPG_SUMMONER_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.name = "Direct";
Direct.value = XPG_SUMMONER_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = XPG_SUMMONER_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.colors_min = 1;
Static.colors_max = 1;
Static.brightness_min = 5;
Static.brightness_max = 64;
Static.colors.resize(1);
modes.push_back(Static);
mode Stars;
Stars.name = "Stars";
Stars.value = XPG_SUMMONER_MODE_STARS;
Stars.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Stars.colors_min = 1;
Stars.colors_max = 1;
Stars.color_mode = MODE_COLORS_MODE_SPECIFIC;
Stars.colors.resize(1);
modes.push_back(Stars);
mode Off;
Off.name = "Off";
Off.value = XPG_SUMMONER_MODE_OFF;
Off.flags = 0;
Off.color_mode = MODE_COLORS_NONE;
modes.push_back(Off);
SetupZones();
}
@@ -321,7 +292,9 @@ void RGBController_XPGSummoner::DeviceUpdateLEDs()
for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++)
{
if(leds[led_idx].value == NA)
{
continue;
}
if(modes[active_mode].color_mode == MODE_COLORS_PER_LED)
{
std::size_t real_idx = leds[led_idx].value;
@@ -330,76 +303,6 @@ void RGBController_XPGSummoner::DeviceUpdateLEDs()
frame_buf[(real_idx * 4) + 2] = RGBGetGValue(colors[led_idx]);
frame_buf[(real_idx * 4) + 3] = RGBGetBValue(colors[led_idx]);
}
else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC && modes[active_mode].value == XPG_SUMMONER_MODE_STATIC)
{
std::size_t real_idx = leds[led_idx].value;
frame_buf[(real_idx * 4) + 0] = modes[active_mode].brightness;
frame_buf[(real_idx * 4) + 1] = RGBGetRValue(modes[active_mode].colors[0]);
frame_buf[(real_idx * 4) + 2] = RGBGetGValue(modes[active_mode].colors[0]);
frame_buf[(real_idx * 4) + 3] = RGBGetBValue(modes[active_mode].colors[0]);
}
else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC && modes[active_mode].value == XPG_SUMMONER_MODE_STARS)
{
for(std::size_t i = 0; i < leds.size(); ++i)
{
std::size_t real_idx = leds[i].value;
frame_buf[(real_idx * 4) + 0] = 0;
frame_buf[(real_idx * 4) + 1] = 0;
frame_buf[(real_idx * 4) + 2] = 0;
frame_buf[(real_idx * 4) + 3] = 0;
}
controller->SendColors(frame_buf, sizeof(frame_buf));
/*---------------------------------------------------*\
| Select a random central LED in the physical matrix |
\*---------------------------------------------------*/
int rows = 6;
int cols = 21;
int center_row = rand() % rows;
int center_col = rand() % cols;
unsigned int center_led = matrix_map[center_row][center_col];
if(center_led != NA)
{
frame_buf[(center_led * 4) + 0] = brightness;
frame_buf[(center_led * 4) + 1] = RGBGetRValue(modes[active_mode].colors[0]);
frame_buf[(center_led * 4) + 2] = RGBGetGValue(modes[active_mode].colors[0]);
frame_buf[(center_led * 4) + 3] = RGBGetBValue(modes[active_mode].colors[0]);
}
/*---------------------------------------------------*\
| Neighbors (fade effect) |
\*---------------------------------------------------*/
unsigned char fade_brightness = 0x32;
int neighbor_offsets[4][2] = { {0, -1}, {0, +1}, {-1, 0}, {+1, 0} };
for(int k = 0; k < 4; ++k)
{
int n_row = center_row + neighbor_offsets[k][0];
int n_col = center_col + neighbor_offsets[k][1];
if(n_row >= 0 && n_row < rows && n_col >= 0 && n_col < cols)
{
unsigned int neighbor_led = matrix_map[n_row][n_col];
if(neighbor_led != NA)
{
frame_buf[(neighbor_led * 4) + 0] = fade_brightness;
frame_buf[(neighbor_led * 4) + 1] = RGBGetRValue(modes[active_mode].colors[0]);
frame_buf[(neighbor_led * 4) + 2] = RGBGetGValue(modes[active_mode].colors[0]);
frame_buf[(neighbor_led * 4) + 3] = RGBGetBValue(modes[active_mode].colors[0]);
}
}
}
controller->SendColors(frame_buf, sizeof(frame_buf));
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
else if(modes[active_mode].color_mode == MODE_COLORS_NONE)
{
std::size_t real_idx = leds[led_idx].value;
frame_buf[(real_idx * 4) + 0] = 0;
frame_buf[(real_idx * 4) + 1] = 0;
frame_buf[(real_idx * 4) + 2] = 0;
frame_buf[(real_idx * 4) + 3] = 0;
}
}
controller->SendColors(frame_buf, sizeof(frame_buf));
}
@@ -27,10 +27,7 @@
enum
{
XPG_SUMMONER_MODE_OFF = 0x00,
XPG_SUMMONER_MODE_DIRECT = 0x01,
XPG_SUMMONER_MODE_STATIC = 0x02,
XPG_SUMMONER_MODE_STARS = 0x03
XPG_SUMMONER_MODE_DIRECT = 0x01
};
class XPGSummonerController
@@ -10,6 +10,8 @@
\*---------------------------------------------------------*/
#include "RGBController_ZotacBlackwellGPU.h"
#include "LogManager.h"
#include "pci_ids.h"
/**------------------------------------------------------------------*\
@name ZOTAC RTX 50 series GPU
@@ -20,21 +22,60 @@
@effects :tools:
@detectors DetectZotacBlackwellGPUControllersPCI
@comment
Supports ZOTAC Blackwell (RTX 50 series) GPUs with 3 zones:
Logo, Side Bar, and Infinity Mirror.
Supports ZOTAC Blackwell (RTX 50 series) GPUs. The zone layout
varies per card and is resolved from a static table keyed on
PCI device and sub-device IDs.
The controller uses individual SMBus byte writes (registers
0x20-0x2F) with a 3ms delay between each transaction.
To add new cards, add PCI ID entries in `pci_ids/pci_ids.h`
and detection entries in
To add new cards, add PCI ID entries in `pci_ids/pci_ids.h`,
a zone config entry in `device_zone_configs` below, and a
`REGISTER_I2C_PCI_DETECTOR` line in
`Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUControllerDetect.cpp`.
\*-------------------------------------------------------------------*/
RGBController_ZotacBlackwellGPU::RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr)
const RGBController_ZotacBlackwellGPU::DeviceZoneConfig RGBController_ZotacBlackwellGPU::device_zone_configs[] =
{
{ NVIDIA_RTX5080_DEV, ZOTAC_RTX5080_AMP_EXTREME_SUB_DEV, { "Logo", "Side Bar", "Infinity Mirror" }, 3 },
{ NVIDIA_RTX5090_DEV, ZOTAC_RTX5090_SOLID_OC_SUB_DEV, { "ZOTAC Gaming", "Logo" }, 2 },
{ 0, 0, { nullptr }, 0 }
};
const RGBController_ZotacBlackwellGPU::DeviceZoneConfig* RGBController_ZotacBlackwellGPU::FindZoneConfig(uint16_t device, uint16_t subdevice)
{
for(const DeviceZoneConfig* cfg = device_zone_configs; cfg->zone_count != 0; cfg++)
{
if(cfg->device == device && cfg->subdevice == subdevice)
{
return cfg;
}
}
return nullptr;
}
RGBController_ZotacBlackwellGPU::RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr,
uint16_t device, uint16_t subdevice)
{
controller = controller_ptr;
const DeviceZoneConfig* cfg = FindZoneConfig(device, subdevice);
if(cfg == nullptr)
{
LOG_ERROR("[%s] Unrecognized PCI device/subdevice: %04X/%04X. Falling back to three generic zones.",
controller->GetName().c_str(), device, subdevice);
zone_names.push_back("Zone 0");
zone_names.push_back("Zone 1");
zone_names.push_back("Zone 2");
}
else
{
for(uint8_t z = 0; z < cfg->zone_count; z++)
{
zone_names.push_back(cfg->zones[z]);
}
}
name = controller->GetName();
vendor = "ZOTAC";
description = "ZOTAC RTX 50 series RGB GPU Device (" + controller->GetVersion() + ")";
@@ -252,52 +293,25 @@ RGBController_ZotacBlackwellGPU::~RGBController_ZotacBlackwellGPU()
void RGBController_ZotacBlackwellGPU::SetupZones()
{
/*---------------------------------------------------------*\
| Zone 0: Logo |
| One single-LED zone per name from the device_zone_configs |
| table. The zone's index is its position here, which is |
| written verbatim to the zone register (0x21) on update. |
\*---------------------------------------------------------*/
zone logo_zone;
logo_zone.name = "Logo";
logo_zone.type = ZONE_TYPE_SINGLE;
logo_zone.leds_min = 1;
logo_zone.leds_max = 1;
logo_zone.leds_count = 1;
logo_zone.matrix_map = NULL;
zones.push_back(logo_zone);
for(const std::string& zone_name : zone_names)
{
zone new_zone;
new_zone.name = zone_name;
new_zone.type = ZONE_TYPE_SINGLE;
new_zone.leds_min = 1;
new_zone.leds_max = 1;
new_zone.leds_count = 1;
new_zone.matrix_map = NULL;
zones.push_back(new_zone);
led logo_led;
logo_led.name = "Logo LED";
leds.push_back(logo_led);
/*---------------------------------------------------------*\
| Zone 1: Side Bar |
\*---------------------------------------------------------*/
zone sidebar_zone;
sidebar_zone.name = "Side Bar";
sidebar_zone.type = ZONE_TYPE_SINGLE;
sidebar_zone.leds_min = 1;
sidebar_zone.leds_max = 1;
sidebar_zone.leds_count = 1;
sidebar_zone.matrix_map = NULL;
zones.push_back(sidebar_zone);
led sidebar_led;
sidebar_led.name = "Side Bar LED";
leds.push_back(sidebar_led);
/*---------------------------------------------------------*\
| Zone 2: Infinity Mirror |
\*---------------------------------------------------------*/
zone infinity_zone;
infinity_zone.name = "Infinity Mirror";
infinity_zone.type = ZONE_TYPE_SINGLE;
infinity_zone.leds_min = 1;
infinity_zone.leds_max = 1;
infinity_zone.leds_count = 1;
infinity_zone.matrix_map = NULL;
zones.push_back(infinity_zone);
led infinity_led;
infinity_led.name = "Infinity Mirror LED";
leds.push_back(infinity_led);
led new_led;
new_led.name = zone_name + " LED";
leds.push_back(new_led);
}
SetupColors();
}
@@ -362,7 +376,7 @@ void RGBController_ZotacBlackwellGPU::DeviceUpdateZone(int zone)
void RGBController_ZotacBlackwellGPU::DeviceUpdateMode()
{
for(unsigned int zone_idx = 0; zone_idx < ZOTAC_BLACKWELL_GPU_NUM_ZONES; zone_idx++)
for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
DeviceUpdateZone(zone_idx);
}
@@ -11,13 +11,17 @@
#pragma once
#include <stdint.h>
#include <string>
#include <vector>
#include "RGBController.h"
#include "ZotacBlackwellGPUController.h"
class RGBController_ZotacBlackwellGPU : public RGBController
{
public:
RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr);
RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr,
uint16_t device, uint16_t subdevice);
~RGBController_ZotacBlackwellGPU();
void SetupZones();
@@ -33,4 +37,17 @@ public:
private:
ZotacBlackwellGPUController* controller;
std::vector<std::string> zone_names;
struct DeviceZoneConfig
{
uint16_t device;
uint16_t subdevice;
const char* zones[4];
uint8_t zone_count;
};
static const DeviceZoneConfig device_zone_configs[];
static const DeviceZoneConfig* FindZoneConfig(uint16_t device, uint16_t subdevice);
};
@@ -46,14 +46,6 @@
#define ZOTAC_BLACKWELL_GPU_REG_RELOAD 0x11
#define ZOTAC_BLACKWELL_GPU_REG_COMMIT 0x17
/*---------------------------------------------------------*\
| Zone indices |
\*---------------------------------------------------------*/
#define ZOTAC_BLACKWELL_GPU_ZONE_LOGO 0x00
#define ZOTAC_BLACKWELL_GPU_ZONE_SIDEBAR 0x01
#define ZOTAC_BLACKWELL_GPU_ZONE_INFINITY 0x02
#define ZOTAC_BLACKWELL_GPU_NUM_ZONES 3
/*---------------------------------------------------------*\
| Mode values (from Firestorm V5.0.0.012E reverse eng.) |
\*---------------------------------------------------------*/
@@ -20,13 +20,13 @@
* DetectZotacBlackwellGPUControllersPCI *
* *
* Detect ZOTAC Blackwell (RTX 50 series) RGB controllers on the enumerated *
* I2C busses at address 0x4B. *
* I2C busses at address 0x4B. Zone configuration is resolved inside the *
* RGBController by looking up the PCI device/sub-device IDs in a static table. *
* *
* bus - pointer to i2c_smbus_interface where RGB device is connected *
* dev - I2C address of RGB device *
* *
\******************************************************************************************/
void DetectZotacBlackwellGPUControllersPCI(i2c_smbus_interface* bus, u8 i2c_addr, const std::string& name)
{
s32 result = bus->i2c_smbus_read_byte_data(i2c_addr, 0x10);
@@ -34,10 +34,13 @@ void DetectZotacBlackwellGPUControllersPCI(i2c_smbus_interface* bus, u8 i2c_addr
if(result >= 0)
{
ZotacBlackwellGPUController* controller = new ZotacBlackwellGPUController(bus, i2c_addr, name);
RGBController_ZotacBlackwellGPU* rgb_controller = new RGBController_ZotacBlackwellGPU(controller);
RGBController_ZotacBlackwellGPU* rgb_controller = new RGBController_ZotacBlackwellGPU(controller,
bus->pci_device,
bus->pci_subsystem_device);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 5080 AMP Extreme INFINITY", DetectZotacBlackwellGPUControllersPCI, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX5080_AMP_EXTREME_SUB_DEV, 0x4B);
REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 5090 SOLID OC", DetectZotacBlackwellGPUControllersPCI, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX5090_SOLID_OC_SUB_DEV, 0x4B);
+24
View File
@@ -0,0 +1,24 @@
/*---------------------------------------------------------*\
| MathUtils.cpp |
| |
| Math utility functions |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <math.h>
#include "MathUtils.h"
int MathUtils::IntInterpolate(int y0, int y1, int x0, int x1, int x)
{
if(x1 == x0)
return y0;
if(y0 == y1)
return y0;
double t = (double)(x - x0) / (double)(x1 - x0);
double y = y0 * (1.0 - t) + y1 * t;
return (int)round(y);
}
+18
View File
@@ -0,0 +1,18 @@
/*---------------------------------------------------------*\
| MathUtils.h |
| |
| Math utility functions |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <stdint.h>
class MathUtils
{
public:
static int IntInterpolate(int y0, int y1, int x0, int x1, int x);
};
+3
View File
@@ -199,6 +199,7 @@ HEADERS +=
serial_port/find_usb_serial_port.h \
serial_port/serial_port.h \
super_io/super_io.h \
MathUtils.h \
StringUtils.h \
SuspendResume/SuspendResume.h \
AutoStart/AutoStart.h \
@@ -265,6 +266,7 @@ SOURCES +=
interop/DeviceGuardManager.cpp \
net_port/net_port.cpp \
serial_port/serial_port.cpp \
MathUtils.cpp \
StringUtils.cpp \
AutoStart/AutoStart.cpp \
KeyboardLayoutManager/KeyboardLayoutManager.cpp \
@@ -437,6 +439,7 @@ win32:contains(QMAKE_TARGET.arch, x86_64) {
copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/SmbusPIIX4.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t)
copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/SmbusI801.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t)
copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/SmbusNCT6793.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t)
copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/SmbusIntelSkylakeIMC.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t)
copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/LpcIO.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t)
first.depends = $(first) copydata
export(first.depends)
+7
View File
@@ -94,3 +94,10 @@ const std::string StringUtils::remove_null_terminating_chars(std::string input)
return(input);
}
std::string StringUtils::u32int_to_hexString(unsigned int value)
{
char hex_str[20] = {0};
snprintf(hex_str, sizeof(hex_str), "%X", value);
return std::string(hex_str);
}
+1
View File
@@ -18,4 +18,5 @@ public:
static std::string wstring_to_string(const std::wstring wstring);
static std::string u16string_to_string(const std::u16string wstring);
static const std::string remove_null_terminating_chars(std::string input);
static std::string u32int_to_hexString(unsigned int value);
};
Binary file not shown.
+14 -3
View File
@@ -147,7 +147,7 @@ bool i2c_smbus_linux_detect()
path[sizeof(path) - 1] = '\0';
strncat(path, ent->d_name, sizeof(path) - strlen(path) - 1);
path[sizeof(path) - 1] = '\0';
if(ent->d_type == DT_LNK)
if(ent->d_type == DT_LNK || ent->d_type == DT_UNKNOWN)
{
ptr = realpath(path, NULL);
if(ptr == NULL)
@@ -155,9 +155,20 @@ bool i2c_smbus_linux_detect()
strncpy(path, ptr, sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
strncat(path, "/..", sizeof(path) - strlen(path) - 1);
path[sizeof(path) - 1] = '\0';
free(ptr);
/*-------------------------------------------------------------*\
| Truncate at last '/' to get the parent PCI device directory. |
| For AMDGPU i2c buses the realpath resolves to something like: |
| /sys/devices/pci.../0000:03:00.0/i2c-4 |
| The parent (0000:03:00.0) contains vendor/device/subsystem |
| files. Using /..' traversal is unreliable in sysfs; directly |
| truncating the path is correct and portable. |
\*-------------------------------------------------------------*/
char* last_slash = strrchr(path, '/');
if(last_slash == NULL || last_slash == path)
continue;
*last_slash = '\0';
}
else
{
+9
View File
@@ -385,6 +385,15 @@ bool i2c_smbus_pawnio_detect()
ResourceManager::get()->RegisterI2CBus(bus);
}
/*-----------------------------------------------------*\
| Try to load Intel Skylake IMC SMBus driver |
\*-----------------------------------------------------*/
if(i2c_smbus_pawnio::start_pawnio("SmbusIntelSkylakeIMC.bin", &pawnio_handle) == S_OK)
{
bus = new i2c_smbus_pawnio(pawnio_handle, "Intel Skylake IMC");
ResourceManager::get()->RegisterI2CBus(bus);
}
return(true);
}
+8
View File
@@ -358,12 +358,15 @@
#define ASUS_TUF_RTX_5060_O8G_GAMING 0x8A1A
#define ASUS_TUF_RTX_5070_O12G_GAMING 0x89F2
#define ASUS_TUF_RTX_5070TI_O16G_GAMING 0x89F4
#define ASUS_TUF_RTX_5070TI_O16G_GAMING_WHITE 0x8A0C
#define ASUS_ROG_STRIX_RTX_5070TI_O16G_GAMING_OC 0x8A0D
#define ASUS_TUF_RTX_5080_O16G_GAMING 0x89D7
#define ASUS_ROG_ASTRAL_RTX_5080_O16G_GAMING 0x89DE
#define ASUS_ROG_ASTRAL_RTX_5080_O16G_GAMING_WHITE 0x8A2B
#define ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING 0x89DF
#define ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING_WHITE 0x8A2C
#define ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING 0x89E3
#define ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_2 0x89E4
#define ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_BTF 0x8A3C
#define ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_WHITE 0x8A2E
#define ASUS_ROG_ASTRAL_LC_RTX_5090_O32G_GAMING 0x89EC
@@ -442,6 +445,8 @@
#define COLORFUL_IGAME_RTX_4080_ULTRAW_OCV2 0x1501
#define COLORFUL_IGAME_RTX_4090_ADVANCED_OCV 0x1400
#define COLORFUL_IGAME_RTX_4090_ADVANCED_OCV2 0x1401
#define COLORFUL_IGAME_RTX_5060_ULTRAW_OC 0x1500
#define COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC_2 0x1530
#define COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC 0x1531
#define COLORFUL_IGAME_RTX_5070_ULTRAW_OCV 0x1500
#define COLORFUL_IGAME_RTX_5070_ULTRAW_OCV2 0x1501
@@ -721,6 +726,7 @@
#define GIGABYTE_RTX5080_GAMING_OC_16G_SUB_DEV 0x4176
#define GIGABYTE_AORUS_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV 0x418A
#define GIGABYTE_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV 0x418B
#define GIGABYTE_AORUS_RTX5080_MASTER_ICE_16G_SUB_DEV 0x418C
#define GIGABYTE_AORUS_RTX5080_MASTER_16G_SUB_DEV 0x4178
#define GIGABYTE_RTX5090_GAMING_OC_32G_SUB_DEV 0x416F
#define GIGABYTE_AORUS_RTX5090_MASTER_32G_SUB_DEV 0x416E
@@ -741,6 +747,7 @@
#define GIGABYTE_RX9070XT_GAMING_16G_SUB_DEV 0x2431
#define GIGABYTE_RX7900GRE_GAMING_OC_16G_SUB_DEV 0x2410
#define GIGABYTE_RX7900XT_GAMING_OC_20G_SUB_DEV 0x240C
#define GIGABYTE_AORUS_RX7900XTX_ELITE_24G_SUB_DEV 0x240D
#define GIGABYTE_RX7600_GAMING_OC_8G_SUB_DEV 0x240B
#define GIGABYTE_RX7900XTX_GAMING_OC_24G_SUB_DEV 0x240E
#define GIGABYTE_RX7600_GAMING_OC_8G_SUB_DEV2 0x242B
@@ -993,6 +1000,7 @@
#define ZOTAC_RTX4090_TRINITY_SUB_DEV 0x3675
#define ZOTAC_RTX4090_AMP_SUB_DEV 0x4675
#define ZOTAC_RTX5080_AMP_EXTREME_SUB_DEV 0x1762
#define ZOTAC_RTX5090_SOLID_OC_SUB_DEV 0x1761
/*-----------------------------------------------------*\
| Manli Sub-Device IDs |
+279
View File
@@ -0,0 +1,279 @@
# OpenRGB MSI Builder for Windows
# PowerShell conversion of build-msi.sh
# Add Wix to path
$env:Path += ";C:\Program Files (x86)\WiX Toolset v3.14\bin"
# Add Qt to path
$env:Path += ";C:\Qt\6.8.3\msvc2022_64\bin"
# Function to generate random UUID v4
function Generate-UUIDv4
{
return [guid]::NewGuid().ToString().ToUpper()
}
# Function to generate namespace-based UUID (SHA1)
function Generate-NamespaceUUID
{
param
(
[guid]$Namespace,
[string]$Name
)
$nameBytes = [System.Text.Encoding]::UTF8.GetBytes($Name)
$combined = $Namespace.ToByteArray() + $nameBytes
$hash = [System.Security.Cryptography.SHA1]::Create().ComputeHash($combined)
# Set version to 5 (SHA1-based namespace UUID)
$hash[6] = ($hash[6] -band 0x0f) -bor 0x50
$hash[8] = ($hash[8] -band 0x3f) -bor 0x80
$uuidBytes = [byte[]]($hash[0..15])
$uuid = [guid]$uuidBytes
return $uuid.ToString().ToUpper()
}
# Configuration
$PRODUCTNAME = "OpenRGB"
$VENDOR = $PRODUCTNAME
$TLD = "org"
$WEBSITE = "https://$($PRODUCTNAME.ToLower()).$TLD"
# URL namespace UUID (RFC 4122, version 4 namespace)
$URLNamespace = [guid]'6ba7b810-9dad-11d1-80b4-00c04fd430c8'
$NAMESPACE = Generate-NamespaceUUID $URLNamespace $WEBSITE
$VENDOR_ID = "$TLD.$VENDOR"
$APP_ID = "$VENDOR_ID.$($PRODUCTNAME.ToLower())"
$GITURL = "https://gitlab.com/CalcProgrammer1/OpenRGB/-"
$GITPARAM = "?inline=false"
$ICONFILE = "qt/OpenRGB.ico"
$LICENSEFILE = "scripts/License.rtf"
$BANNERIMAGE = "scripts/banner.bmp"
$DIALOGBACKGROUND = "scripts/dialog_background.bmp"
$PROJECT_FILE = "OpenRGB.pro"
$XMLOUTFILE = "$($PRODUCTNAME.ToLower()).wxs"
# Extract version from project file
$VERSION = & qmake $PROJECT_FILE 2>&1 | Select-String 'VERSION_WIX' | Select-Object -First 1 | ForEach-Object {
$_.Line -split ':' | Select-Object -Last 1 | ForEach-Object { $_.Trim() }
}
# Check for suffix to determine channel
$CHANNEL = ""
$SUFFIX = Select-String -Path $PROJECT_FILE -Pattern 'SUFFIX\s' | ForEach-Object {
$_.Line -split '=' | Select-Object -Last 1 | ForEach-Object { $_.Trim() }
}
if($SUFFIX)
{
$CHANNEL = "-git"
}
# Calculate upgrade code
$UPGRADECODE = Generate-NamespaceUUID ([guid]$NAMESPACE) $APP_ID
$PRODUCTCOMMENT = "Open source RGB lighting control that doesn't depend on manufacturer software."
# Print Metadata to the log
Write-Host "Icon URL:`t" $GITURL$ICONFILE
Write-Host "License URL:`t" $GITURL$LICENSEFILE
Write-Host "AppID - Channel:`t" $APP_ID " - " $CHANNEL
Write-Host "Upgrade UUID:`t" $UPGRADECODE
Write-Host "Product Name:`t" $PRODUCTNAME
Write-Host "Vendor - VendorID:`t`t" $VENDOR " - " $VENDOR_ID
Write-Host "Version:`t" $VERSION
# Copy build directory to working path
$BUILT_PATH = "OpenRGB Windows 64-bit\*"
$WORKING_PATH = "OpenRGB Windows MSI Installer\"
if(Test-Path $BUILT_PATH)
{
Copy-Item -Path $BUILT_PATH -Destination $WORKING_PATH -Recurse -Force
}
$count = 1
$EXE_FILE = ""
$SVC_FILE = ""
$FILES = ""
$COMPONENTS = ""
$DIRECTORIES = ""
# Build file list for files in the working path
Get-ChildItem -Path $WORKING_PATH -Force | ForEach-Object {
$file = $_
$filename = $_.Name
if($filename -eq "$PRODUCTNAME.exe")
{
# If this is the executable, treat as special case
$script:EXE_ID = "$($PRODUCTNAME)00"
$script:SVC_ID = "$($PRODUCTNAME)00_SVC"
$script:EXE_FILE = " <File Id='$($script:EXE_ID)' Source='$WORKING_PATH$filename' KeyPath='yes'/>`r`n"
$script:SVC_FILE = " <File Id='$($script:SVC_ID)' Source='$WORKING_PATH$filename' KeyPath='yes'/>`r`n"
}
elseif($_.PSIsContainer)
{
# If this is a directory, add another component
$script:COMPONENTS += " <ComponentRef Id='$($filename)Files'/>`r`n"
$guid = Generate-UUIDv4
$TEMP = " <Directory Id='$filename' Name='$filename'>`r`n"
$TEMP += " <Component Id='$($filename)Files' Guid='$guid'>`r`n"
Get-ChildItem -Path $file.FullName -Force | ForEach-Object {
$filename2 = $_.Name
$TEMP += " <File Id='$($PRODUCTNAME)$($script:count)' Source='$WORKING_PATH$filename/$filename2'/>`r`n"
$script:count++
}
$TEMP += " </Component>`r`n </Directory>`r`n"
$script:DIRECTORIES += $TEMP
}
else
{
# Any other file to files list
$script:FILES += " <File Id='$($PRODUCTNAME)$($script:count)' Source='$WORKING_PATH$filename'/>`r`n"
$script:count++
}
}
# Create the Wix XML file
$XML_FILE = ""
$XML_FILE += "<?xml version='1.0' encoding='windows-1252'?>`r`n"
$XML_FILE += "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>`r`n"
$XML_FILE += " <Product Name='$PRODUCTNAME' Manufacturer='$VENDOR'`r`n"
$XML_FILE += " Id='*'`r`n"
$XML_FILE += " UpgradeCode='$UPGRADECODE'`r`n"
$XML_FILE += " Language='1033' Codepage='1252' Version='$VERSION'>`r`n"
$XML_FILE += " <Package Keywords='Installer' Description='$PRODUCTNAME Installer'`r`n"
$XML_FILE += " Comments=`"$PRODUCTCOMMENT`" Manufacturer='OpenRGB'`r`n"
$XML_FILE += " InstallerVersion='200' Languages='1033' Compressed='yes' SummaryCodepage='1252' Platform='x64'/>`r`n"
$XML_FILE += " <Media Id='1' Cabinet='$($PRODUCTNAME.ToLower()).cab' EmbedCab='yes'/>`r`n"
$XML_FILE += " <Condition Message='This package supports Windows 64bit Only'>VersionNT64</Condition>`r`n"
$XML_FILE += " <MajorUpgrade Schedule='afterInstallInitialize' AllowDowngrades='yes'/>`r`n"
$XML_FILE += " <Icon Id='OpenRGBIcon' SourceFile='$ICONFILE'/>`r`n"
$XML_FILE += " <Property Id='ARPPRODUCTICON' Value='OpenRGBIcon'/>`r`n"
$XML_FILE += " <Property Id='ARPURLINFOABOUT' Value='https://www.openrgb.org'/>`r`n"
$XML_FILE += " <Property Id='WIXUI_INSTALLDIR' Value='INSTALLDIR'/>`r`n"
$XML_FILE += " <WixVariable Id='WixUILicenseRtf' Value='$LICENSEFILE'/>`r`n"
$XML_FILE += " <WixVariable Id='WixUIBannerBmp' Value='$BANNERIMAGE'/>`r`n"
$XML_FILE += " <WixVariable Id='WixUIDialogBmp' Value='$DIALOGBACKGROUND'/>`r`n"
$XML_FILE += "`r`n"
$XML_FILE += " <UI>`r`n"
$XML_FILE += " <UIRef Id='WixUI_InstallDir'/>`r`n"
$XML_FILE += " <UIRef Id='WixUI_ErrorProgressText'/>`r`n"
$XML_FILE += "`r`n"
$XML_FILE += " <Dialog Id='PawnIODialog' Width='370' Height='270' Title='!(loc.InstallDirDlg_Title)'>`r`n"
$XML_FILE += " <Control Id='Next' Type='PushButton' X='236' Y='243' Width='56' Height='17' Default='yes' Text='!(loc.WixUINext)' />`r`n"
$XML_FILE += " <Control Id='Back' Type='PushButton' X='180' Y='243' Width='56' Height='17' Text='!(loc.WixUIBack)' />`r`n"
$XML_FILE += " <Control Id='Cancel' Type='PushButton' X='304' Y='243' Width='56' Height='17' Cancel='yes' Text='!(loc.WixUICancel)'>`r`n"
$XML_FILE += " <Publish Event='SpawnDialog' Value='CancelDlg' />`r`n"
$XML_FILE += " </Control>`r`n"
$XML_FILE += " <Control Id='Description' Type='Text' X='25' Y='23' Width='280' Height='15' Transparent='yes' NoPrefix='yes' Text='Install PawnIO for RGB RAM and Motherboard control.' />`r`n"
$XML_FILE += " <Control Id='Title' Type='Text' X='15' Y='6' Width='200' Height='15' Transparent='yes' NoPrefix='yes' Text='PawnIO' />`r`n"
$XML_FILE += " <Control Id='BannerBitmap' Type='Bitmap' X='0' Y='0' Width='370' Height='44' TabSkip='no' Text='!(loc.InstallDirDlgBannerBitmap)' />`r`n"
$XML_FILE += " <Control Id='BannerLine' Type='Line' X='0' Y='44' Width='373' Height='0' />`r`n"
$XML_FILE += " <Control Id='BottomLine' Type='Line' X='0' Y='234' Width='373' Height='0' />`r`n"
$XML_FILE += " <Control Id='MainText' Type='Text' X='40' Y='60' Width='300' Height='40' Text='OpenRGB uses the PawnIO driver to control RGB lighting on DRAM modules and some older motherboards. For these devices to work, you must manually install PawnIO by visiting the link below.' />`r`n"
$XML_FILE += " <Control Id='SecondText' Type='Text' X='40' Y='100' Width='300' Height='40' Text='If you do not need support for these devices, you can safely skip this step and continue with the installation.' />`r`n"
$XML_FILE += " <Control Id='MainLink' Type='Hyperlink' X='40' Y='140' Width='300' Height='20' Text='&lt;a href=`"https://pawnio.eu`"&gt;https://pawnio.eu&lt;/a&gt;' />`r`n"
$XML_FILE += " <Control Id='ThirdText' Type='Text' X='40' Y='180' Width='300' Height='40' Text='With PawnIO installed, OpenRGB must be run as administrator to access these devices.' />`r`n"
$XML_FILE += " </Dialog>`r`n"
$XML_FILE += "`r`n"
$XML_FILE += " <Dialog Id='ServiceDialog' Width='370' Height='270' Title='!(loc.InstallDirDlg_Title)'>`r`n"
$XML_FILE += " <Control Id='Next' Type='PushButton' X='236' Y='243' Width='56' Height='17' Default='yes' Text='!(loc.WixUINext)' />`r`n"
$XML_FILE += " <Control Id='Back' Type='PushButton' X='180' Y='243' Width='56' Height='17' Text='!(loc.WixUIBack)' />`r`n"
$XML_FILE += " <Control Id='Cancel' Type='PushButton' X='304' Y='243' Width='56' Height='17' Cancel='yes' Text='!(loc.WixUICancel)'>`r`n"
$XML_FILE += " <Publish Event='SpawnDialog' Value='CancelDlg' />`r`n"
$XML_FILE += " </Control>`r`n"
$XML_FILE += "`r`n"
$XML_FILE += " <Control Id='Description' Type='Text' X='25' Y='23' Width='280' Height='15' Transparent='yes' NoPrefix='yes' Text='Choose whether to register $PRODUCTNAME as a system service.' />`r`n"
$XML_FILE += " <Control Id='Title' Type='Text' X='15' Y='6' Width='200' Height='15' Transparent='yes' NoPrefix='yes' Text='$PRODUCTNAME Service' />`r`n"
$XML_FILE += " <Control Id='BannerBitmap' Type='Bitmap' X='0' Y='0' Width='370' Height='44' TabSkip='no' Text='!(loc.InstallDirDlgBannerBitmap)' />`r`n"
$XML_FILE += " <Control Id='BannerLine' Type='Line' X='0' Y='44' Width='373' Height='0' />`r`n"
$XML_FILE += " <Control Id='BottomLine' Type='Line' X='0' Y='234' Width='373' Height='0' />`r`n"
$XML_FILE += "`r`n"
$XML_FILE += " <Control Id='TypicalText' Type='Text' X='40' Y='60' Width='300' Height='40' Text='Installing OpenRGB without registering as a system service means nothing runs automatically in the background by default. Accessing devices through PawnIO will require running OpenRGB as administrator.' />`r`n"
$XML_FILE += " <Control Id='TypicalButton' Type='PushButton' X='40' Y='100' Width='200' Height='17' ToolTip='Does not register OpenRGB as a system service. Accessing certain devices will require running the application as administrator.' Default='yes' Text='Do not Install System Service (Default)'>`r`n"
$XML_FILE += " <Publish Property='WixUI_InstallMode' Value='InstallTypical'>1</Publish>`r`n"
$XML_FILE += " <Publish Event='SetInstallLevel' Value='1'>1</Publish>`r`n"
$XML_FILE += " <Publish Event='NewDialog' Value='VerifyReadyDlg'>1</Publish>`r`n"
$XML_FILE += " </Control>`r`n"
$XML_FILE += " <Control Id='CompleteText' Type='Text' X='40' Y='120' Width='300' Height='40' Text='Installing OpenRGB as a system service means the application will run automatically in the background. Accessing devices through PawnIO will not require running OpenRGB as administrator.' />`r`n"
$XML_FILE += " <Control Id='CompleteWarning' Type='Text' X='40' Y='160' Width='300' Height='40' Text='Warning: Installing OpenRGB as a system service is experimental in the current release. Settings do not synchronize between the user interface and the service. If you encounter issues with the service, you can uninstall it and use the non-service installation instead.' />`r`n"
$XML_FILE += " <Control Id='CompleteButton' Type='PushButton' X='40' Y='210' Width='200' Height='17' ToolTip='Registers OpenRGB system service, allowing access to devices which require administrator access without having to run the application as administrator.' Text='Install System Service'>`r`n"
$XML_FILE += " <Publish Property='WixUI_InstallMode' Value='InstallComplete'>1</Publish>`r`n"
$XML_FILE += " <Publish Event='SetInstallLevel' Value='2'>2</Publish>`r`n"
$XML_FILE += " <Publish Event='NewDialog' Value='VerifyReadyDlg'>1</Publish>`r`n"
$XML_FILE += " </Control>`r`n"
$XML_FILE += " </Dialog>`r`n"
$XML_FILE += "`r`n"
$XML_FILE += " <Publish Dialog='InstallDirDlg' Control='Next' Event='NewDialog' Value='PawnIODialog' Order='5'>WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID=`"1`"</Publish>`r`n"
$XML_FILE += " <Publish Dialog='VerifyReadyDlg' Control='Back' Event='NewDialog' Value='ServiceDialog' Order='2'>1</Publish>`r`n"
$XML_FILE += " <Publish Dialog='PawnIODialog' Control='Next' Event='NewDialog' Value='ServiceDialog' Order='2'>1</Publish>`r`n"
$XML_FILE += " <Publish Dialog='PawnIODialog' Control='Back' Event='NewDialog' Value='InstallDirDlg' Order='2'>1</Publish>`r`n"
$XML_FILE += " <Publish Dialog='ServiceDialog' Control='Next' Event='NewDialog' Value='VerifyReadyDlg' Order='2'>1</Publish>`r`n"
$XML_FILE += " <Publish Dialog='ServiceDialog' Control='Back' Event='NewDialog' Value='PawnIODialog' Order='2'>1</Publish>`r`n"
$XML_FILE += " </UI>`r`n"
$XML_FILE += "`r`n"
$XML_FILE += " <Directory Id='TARGETDIR' Name='SourceDir'>`r`n"
$XML_FILE += " <Directory Id='ProgramFiles64Folder'>`r`n"
$XML_FILE += " <Directory Id='INSTALLDIR' Name='$PRODUCTNAME'>`r`n"
$guid1 = Generate-UUIDv4
$XML_FILE += " <Component Id='$($PRODUCTNAME)Files' Guid='$guid1'>`r`n"
$XML_FILE += $EXE_FILE
$XML_FILE += $FILES
$XML_FILE += " </Component>`r`n"
$guid2 = Generate-UUIDv4
$XML_FILE += " <Component Id='$($PRODUCTNAME)Service' Guid='$guid2'>`r`n"
$XML_FILE += $SVC_FILE
$XML_FILE += " <ServiceInstall Id='ServiceInstaller' Type='ownProcess' Name='OpenRGB' DisplayName='OpenRGB' Description='OpenRGB SDK Server' Start='auto' Account='[SERVICEACCOUNT]' Password='[SERVICEPASSWORD]' ErrorControl='normal'/>`r`n"
$XML_FILE += " <ServiceControl Id='StartService' Start='install' Stop='both' Remove='uninstall' Name='OpenRGB' Wait='yes'/>`r`n"
$XML_FILE += " </Component>`r`n"
$XML_FILE += $DIRECTORIES
$XML_FILE += " </Directory>`r`n"
$XML_FILE += " </Directory>`r`n"
$XML_FILE += " <Directory Id='ProgramMenuFolder' Name='Programs'>`r`n"
$guid3 = Generate-UUIDv4
$XML_FILE += " <Component Id='ProgramMenuShortcut' Guid='$guid3'>`r`n"
$XML_FILE += " <Shortcut Id='startmenu$PRODUCTNAME' Directory='ProgramMenuFolder' Name='$PRODUCTNAME' Target='[#$EXE_ID]' WorkingDirectory='INSTALLDIR' Advertise='no'/>`r`n"
$XML_FILE += " <RemoveFile Id='ProgramMenuShortcut' Name='$PRODUCTNAME.lnk' Directory='ProgramMenuFolder' On='uninstall'/>`r`n"
$XML_FILE += " <RegistryValue Root='HKCU' Key='Software\[Manufacturer]\[ProductName]' Type='string' Value='' KeyPath='yes'/>`r`n"
$XML_FILE += " </Component>`r`n"
$XML_FILE += " </Directory>`r`n"
$XML_FILE += " </Directory>`r`n"
$XML_FILE += " <Feature Id='$($PRODUCTNAME)Application' Title='$PRODUCTNAME' Description='Install the $PRODUCTNAME application files.' Level='1' AllowAdvertise='no' ConfigurableDirectory='INSTALLDIR' InstallDefault='local'>`r`n"
$XML_FILE += " <ComponentRef Id='$($PRODUCTNAME)Files'/>`r`n"
$XML_FILE += $COMPONENTS
$XML_FILE += " <ComponentRef Id='ProgramMenuShortcut'/>`r`n"
$XML_FILE += " </Feature>`r`n"
$XML_FILE += " <Feature Id='$($PRODUCTNAME)RegisterService' Title='$PRODUCTNAME' Description='Register $PRODUCTNAME as a system service.' Display='expand' Level='2' ConfigurableDirectory='INSTALLDIR'>`r`n"
$XML_FILE += " <ComponentRef Id='$($PRODUCTNAME)Service'/>`r`n"
$XML_FILE += " </Feature>`r`n"
$XML_FILE += " </Product>`r`n"
$XML_FILE += "</Wix>"
# Write XML file
$XML_FILE | Out-File -FilePath $XMLOUTFILE -Encoding UTF8
# Print the XML for debugging
Write-Host (Get-Content $XMLOUTFILE)
# Build the MSI package
Write-Host "`nBuilding MSI package..."
$wixObjFile = "$($PRODUCTNAME.ToLower()).wixobj"
$msiFile = "$($PRODUCTNAME)_Windows_64.msi"
& candle -arch x64 $XMLOUTFILE
& light -sval -ext WixUIExtension $wixObjFile -out $msiFile
+29 -7
View File
@@ -115,6 +115,23 @@ XML_FILE+=" <UI>\r\n"
XML_FILE+=" <UIRef Id='WixUI_InstallDir'/>\r\n"
XML_FILE+=" <UIRef Id='WixUI_ErrorProgressText'/>\r\n"
XML_FILE+="\r\n"
XML_FILE+=" <Dialog Id='PawnIODialog' Width='370' Height='270' Title='!(loc.InstallDirDlg_Title)'>\r\n"
XML_FILE+=" <Control Id='Next' Type='PushButton' X='236' Y='243' Width='56' Height='17' Default='yes' Text='!(loc.WixUINext)' />\r\n"
XML_FILE+=" <Control Id='Back' Type='PushButton' X='180' Y='243' Width='56' Height='17' Text='!(loc.WixUIBack)' />\r\n"
XML_FILE+=" <Control Id='Cancel' Type='PushButton' X='304' Y='243' Width='56' Height='17' Cancel='yes' Text='!(loc.WixUICancel)'>\r\n"
XML_FILE+=" <Publish Event='SpawnDialog' Value='CancelDlg' />\r\n"
XML_FILE+=" </Control>\r\n"
XML_FILE+=" <Control Id='Description' Type='Text' X='25' Y='23' Width='280' Height='15' Transparent='yes' NoPrefix='yes' Text='Install PawnIO for RGB RAM and Motherboard control.' />\r\n"
XML_FILE+=" <Control Id='Title' Type='Text' X='15' Y='6' Width='200' Height='15' Transparent='yes' NoPrefix='yes' Text='PawnIO' />\r\n"
XML_FILE+=" <Control Id='BannerBitmap' Type='Bitmap' X='0' Y='0' Width='370' Height='44' TabSkip='no' Text='!(loc.InstallDirDlgBannerBitmap)' />\r\n"
XML_FILE+=" <Control Id='BannerLine' Type='Line' X='0' Y='44' Width='373' Height='0' />\r\n"
XML_FILE+=" <Control Id='BottomLine' Type='Line' X='0' Y='234' Width='373' Height='0' />\r\n"
XML_FILE+=" <Control Id='MainText' Type='Text' X='40' Y='60' Width='300' Height='40' Text='OpenRGB uses the PawnIO driver to control RGB lighting on DRAM modules and some older motherboards. For these devices to work, you must manually install PawnIO by visiting the link below.' />\r\n"
XML_FILE+=" <Control Id='SecondText' Type='Text' X='40' Y='100' Width='300' Height='40' Text='If you do not need support for these devices, you can safely skip this step and continue with the installation.' />\r\n"
XML_FILE+=" <Control Id='MainLink' Type='Hyperlink' X='40' Y='140' Width='300' Height='20' Text='&lt;a href=\"https://pawnio.eu\"&gt;https://pawnio.eu&lt;/a&gt;' />\r\n"
XML_FILE+=" <Control Id='ThirdText' Type='Text' X='40' Y='180' Width='300' Height='40' Text='With PawnIO installed, OpenRGB must be run as administrator to access these devices.' />\r\n"
XML_FILE+=" </Dialog>\r\n"
XML_FILE+="\r\n"
XML_FILE+=" <Dialog Id='ServiceDialog' Width='370' Height='270' Title='!(loc.InstallDirDlg_Title)'>\r\n"
XML_FILE+=" <Control Id='Next' Type='PushButton' X='236' Y='243' Width='56' Height='17' Default='yes' Text='!(loc.WixUINext)' />\r\n"
XML_FILE+=" <Control Id='Back' Type='PushButton' X='180' Y='243' Width='56' Height='17' Text='!(loc.WixUIBack)' />\r\n"
@@ -122,28 +139,33 @@ XML_FILE+=" <Control Id='Cancel' Type='PushButton' X='304' Y='243
XML_FILE+=" <Publish Event='SpawnDialog' Value='CancelDlg' />\r\n"
XML_FILE+=" </Control>\r\n"
XML_FILE+="\r\n"
XML_FILE+=" <Control Id='Description' Type='Text' X='25' Y='23' Width='280' Height='15' Transparent='yes' NoPrefix='yes' Text='Choose whether to register ${PRODUCTNAME} as a system service.' />\r\n"
XML_FILE+=" <Control Id='Title' Type='Text' X='15' Y='6' Width='200' Height='15' Transparent='yes' NoPrefix='yes' Text='${PRODUCTNAME} Service' />\r\n"
XML_FILE+=" <Control Id='Description' Type='Text' X='25' Y='23' Width='280' Height='15' Transparent='yes' NoPrefix='yes' Text='Choose whether to register $PRODUCTNAME as a system service.' />\r\n"
XML_FILE+=" <Control Id='Title' Type='Text' X='15' Y='6' Width='200' Height='15' Transparent='yes' NoPrefix='yes' Text='$PRODUCTNAME Service' />\r\n"
XML_FILE+=" <Control Id='BannerBitmap' Type='Bitmap' X='0' Y='0' Width='370' Height='44' TabSkip='no' Text='!(loc.InstallDirDlgBannerBitmap)' />\r\n"
XML_FILE+=" <Control Id='BannerLine' Type='Line' X='0' Y='44' Width='373' Height='0' />\r\n"
XML_FILE+=" <Control Id='BottomLine' Type='Line' X='0' Y='234' Width='373' Height='0' />\r\n"
XML_FILE+="\r\n"
XML_FILE+=" <Control Id='TypicalButton' Type='PushButton' X='40' Y='65' Width='200' Height='17' ToolTip='Does not register OpenRGB as a system service. Accessing certain devices will require running the application as administrator.' Default='yes' Text='Do not Install System Service (Default)'>\r\n"
XML_FILE+=" <Control Id='TypicalText' Type='Text' X='40' Y='60' Width='300' Height='40' Text='Installing OpenRGB without registering as a system service means nothing runs automatically in the background by default. Accessing devices through PawnIO will require running OpenRGB as administrator.' />\r\n"
XML_FILE+=" <Control Id='TypicalButton' Type='PushButton' X='40' Y='100' Width='200' Height='17' ToolTip='Does not register OpenRGB as a system service. Accessing certain devices will require running the application as administrator.' Default='yes' Text='Do not Install System Service (Default)'>\r\n"
XML_FILE+=" <Publish Property='WixUI_InstallMode' Value='InstallTypical'>1</Publish>\r\n"
XML_FILE+=" <Publish Event='SetInstallLevel' Value='1'>1</Publish>\r\n"
XML_FILE+=" <Publish Event='NewDialog' Value='VerifyReadyDlg'>1</Publish>\r\n"
XML_FILE+=" </Control>\r\n"
XML_FILE+=" <Control Id='CompleteButton' Type='PushButton' X='40' Y='171' Width='200' Height='17' ToolTip='Registers OpenRGB system service, allowing access to devices which require administrator access without having to run the application as administrator.' Text='Install System Service'>\r\n"
XML_FILE+=" <Control Id='CompleteText' Type='Text' X='40' Y='120' Width='300' Height='40' Text='Installing OpenRGB as a system service means the application will run automatically in the background. Accessing devices through PawnIO will not require running OpenRGB as administrator.' />\r\n"
XML_FILE+=" <Control Id='CompleteWarning' Type='Text' X='40' Y='160' Width='300' Height='40' Text='Warning: Installing OpenRGB as a system service is experimental in the current release. Settings do not synchronize between the user interface and the service. If you encounter issues with the service, you can uninstall it and use the non-service installation instead.' />\r\n"
XML_FILE+=" <Control Id='CompleteButton' Type='PushButton' X='40' Y='210' Width='200' Height='17' ToolTip='Registers OpenRGB system service, allowing access to devices which require administrator access without having to run the application as administrator.' Text='Install System Service'>\r\n"
XML_FILE+=" <Publish Property='WixUI_InstallMode' Value='InstallComplete'>1</Publish>\r\n"
XML_FILE+=" <Publish Event='SetInstallLevel' Value='2'>1</Publish>\r\n"
XML_FILE+=" <Publish Event='SetInstallLevel' Value='2'>2</Publish>\r\n"
XML_FILE+=" <Publish Event='NewDialog' Value='VerifyReadyDlg'>1</Publish>\r\n"
XML_FILE+=" </Control>\r\n"
XML_FILE+=" </Dialog>\r\n"
XML_FILE+="\r\n"
XML_FILE+=" <Publish Dialog='InstallDirDlg' Control='Next' Event='NewDialog' Value='ServiceDialog' Order='5'>WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID=\"1\"</Publish>\r\n"
XML_FILE+=" <Publish Dialog='InstallDirDlg' Control='Next' Event='NewDialog' Value='PawnIODialog' Order='5'>WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID=\"1\"</Publish>\r\n"
XML_FILE+=" <Publish Dialog='VerifyReadyDlg' Control='Back' Event='NewDialog' Value='ServiceDialog' Order='2'>1</Publish>\r\n"
XML_FILE+=" <Publish Dialog='PawnIODialog' Control='Next' Event='NewDialog' Value='ServiceDialog' Order='2'>1</Publish>\r\n"
XML_FILE+=" <Publish Dialog='PawnIODialog' Control='Back' Event='NewDialog' Value='InstallDirDlg' Order='2'>1</Publish>\r\n"
XML_FILE+=" <Publish Dialog='ServiceDialog' Control='Next' Event='NewDialog' Value='VerifyReadyDlg' Order='2'>1</Publish>\r\n"
XML_FILE+=" <Publish Dialog='ServiceDialog' Control='Back' Event='NewDialog' Value='InstallDirDlg' Order='2'>1</Publish>\r\n"
XML_FILE+=" <Publish Dialog='ServiceDialog' Control='Back' Event='NewDialog' Value='PawnIODialog' Order='2'>1</Publish>\r\n"
XML_FILE+=" </UI>\r\n"
XML_FILE+="\r\n"
XML_FILE+=" <Directory Id='TARGETDIR' Name='SourceDir'>\r\n"