Compare commits

...
Author SHA1 Message Date
SalastilandClaude Opus 5 f888b1ddbc Fix Corsair K70 RGB PRO V2 direct lighting
The K70 RGB PRO V2 (1B1C:1BB3) was detected but never lit: it blanked
into software render mode and then fell back to its onboard effect.
Getting it working turned up five faults, three of which affect every
Corsair V2 device rather than just this keyboard.

Packet size is now read from the HID report descriptor before the first
write instead of being inferred from a reply. These devices come in a 64
byte and a 1024 byte flavour, and sending a short packet to a 1024 byte
endpoint stalls it until the device is power cycled, so the size has to
be known up front rather than after two short queries have gone out.

CORSAIR_V2_PACKET_SIZE was 1024 while pkt_sze reached 1025, so every
read using pkt_sze overran its stack buffer by a byte. The command
helpers now use pkt_sze against buffers of the corrected size.

The lighting resource probe ran before the device was placed in software
render mode, where the answer is meaningless, and treated any error as
"use resource 1". Devices that answer invalid or unsupported for
resource 1 want the alternate lighting resource, which takes RGB
triplets; this keyboard is one of them. The probe now runs after the
render mode switch and distinguishes a stale open handle from an
unsupported resource.

Direct lighting writes were sized from the keymap, which covers only the
keys that exist. The hardware expects its full slot count and ignores a
short write, so corsair_v2_device carries an optional hw_led_count and
the buffer map is padded to it. Set to 193 for this keyboard.

The keepalive thread wrote LEDs on its first pass, because
last_update_time was left at the clock epoch and so always read as older
than the update period. That raced detection on the same HID handle with
no locking, which both corrupted the transaction and could abort the
process. The clock is now started before the thread runs, device
transactions are serialised behind a mutex, and the thread is stopped
before Shutdown rather than after.

Also fixes an uninitialised stack buffer sent to the device during
setup, and widens the K70 RGB PRO matrix to 22 columns so the numpad
period key has a position in the layout instead of being dropped.

Verified on hardware: repeated colour changes apply and the device stays
responsive across runs.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-21 21:08:51 -04:00
Alzeroff-The-Allfather f03b3472ea Add Corsair K60 RGB PRO SE support 2026-09-21 12:01:20 -05:00
Kamil Rojewski 3e53302613 Apply active profile plugin data when the GUI loads its plugins 2026-09-21 07:20:47 -05:00
Kamil Rojewski 30043e3835 Do not clear the active profile when a profile cannot be read 2026-09-21 07:20:14 +02:00
André Schwarz ce3ee27d8e Renaming PAGE_SIZE in LogitechHIDPP20Controller.cpp 2026-09-20 23:20:08 -05:00
Dmitry Kychanov 0873907793 Fix potential race condition in log message 2026-09-21 03:26:28 +04:00
rom4ster 5303e29628 Remove Debug and Extra Line 2026-09-20 15:33:55 -05:00
rom4ster ffcf003c37 Add Support for Razer Naga V3 Pro 2026-09-20 15:33:54 -05:00
ubelhj f7e0e3d77b Add MSI PRO B850-S WIFI6E to supported devices 2026-09-20 12:45:15 -05:00
Adam Honse cb0052c761 Add a mutex to protect against concurrent saving of configuration file that could result in corruption of the file 2026-09-19 15:18:47 -05:00
Jan Dvořák e075870853 Improve Intel GPU I2C bus detection 2026-09-19 14:36:31 -05:00
Victor Ferreira 4cf5fe9346 Correct the swapped brightness and speed comments
Measured on an ASRock Intel Arc B580 Steel Legend, which uses this same
packet: holding everything else fixed and stepping data[5] through 0xFF,
0x40 and 0x00 dims the zone progressively and turns it off at zero, while
data[4] does not change brightness.  So data[4] is the effect speed and
data[5] is the brightness, the other way around from what the comments
said.  The bytes written are unchanged, this only fixes the labels.
2026-09-19 14:35:25 -05:00
Victor Ferreira eb5d88537d Call Shutdown() in the ASRock GPU controller destructor
Every other ASRock controller shuts the device thread down before deleting
the controller.  This one did not, so tearing it down logs

  Device thread still active in base class destructor, ensure Shutdown()
  was called from inherited destructor

on every exit.  Seen on an ASRock Intel Arc B580 Steel Legend, but the file
is shared with the Radeon Steel Legend and Taichi cards.
2026-09-19 14:35:25 -05:00
Adam Honse d5e0c94fdc Fix LED view hiding if a device supports per-zone modes and a zone is selected that is following device mode while the device mode is per-LED 2026-09-19 14:23:37 -05:00
Petre cd44e41d46 [New Device] Gigabyte GeForce RTX 4070 SUPER Eagle OC (non-ICE) 2026-09-18 14:02:43 -05:00
epicchillman 0129e58f53 Add support for MSI PRO B850M-A WIFI motherboard 2026-09-18 01:04:25 -07:00
Adam Honse 0adfde971b Update PalitGPUControllerDetect to filter for port ID 1 (the NVIDIA OEM bus) and re-add the PALIT string check in addition to the Gainward i2c_smbus_write_quick check for V1 controllers 2026-09-18 00:47:41 -05:00
Adam Honse 27a93edec5 Add second ID for PNY RTX 5070TI ARGB Epic-X OC 2026-09-18 00:38:50 -05:00
Adam Honse 65e04a5332 Fix crash in MSI 185 controller when board config is null, fixes #5869 2026-09-18 00:24:19 -05:00
Eder Sánchez ec5ff26eda Migrate pre-1.0 AutoStart and UserInterface settings 2026-09-18 00:08:10 -05:00
Prasad Patole 51398fd6c5 Added Support for ASRock RX 9070 XT Steel Legend Dark (0x5414) 2026-09-17 23:45:02 -05:00
Adam Honse 9a6cfaf021 Add visible field to schema for hiding entries from the UI. Use it to hide the no show dialog hashes 2026-09-17 20:39:33 -05:00
Adam Honse 0a201edbc1 Fix a null pointer dereference in SendReply_PluginList 2026-09-17 16:48:41 -05:00
Adam Honse 83ea112603 Update drivers settings defaults 2026-09-16 12:04:43 -05:00
36 changed files with 851 additions and 159 deletions
@@ -186,8 +186,8 @@ void ASRockGPUSMBusController::SetChannelColor(uint8_t channel_idx, uint8_t red,
red,
green,
blue,
0x80, // Brightness
0xFF, // Effect speed
0x80, // Effect speed
0xFF, // Brightness
0x00, // Effect direction
0x1A // Unknown constant
};
@@ -42,5 +42,6 @@ DetectedControllers DetectASRockGPUSMBusControllers(i2c_smbus_interface* bus, ui
}
REGISTER_I2C_PCI_DETECTOR("ASRock Radeon RX 9070 XT Steel Legend", DetectASRockGPUSMBusControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, ASROCK_SUB_VEN, 0x5403, ASROCK_GPU_SMBUS_ADDRESS);
REGISTER_I2C_PCI_DETECTOR("ASRock Radeon RX 9070 XT Steel Legend Dark", DetectASRockGPUSMBusControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, ASROCK_SUB_VEN, 0x5414, ASROCK_GPU_SMBUS_ADDRESS);
REGISTER_I2C_PCI_DETECTOR("ASRock Radeon RX 9070 XT Taichi 16GB OC", DetectASRockGPUSMBusControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, ASROCK_SUB_VEN, 0x5401, ASROCK_GPU_SMBUS_ADDRESS);
REGISTER_I2C_PCI_DETECTOR("ASRock Radeon RX 9060 XT Steel Legend", DetectASRockGPUSMBusControllers, AMD_GPU_VEN, AMD_NAVI44_DEV, ASROCK_SUB_VEN, 0x5407, ASROCK_GPU_SMBUS_ADDRESS);
@@ -44,6 +44,8 @@ RGBController_ASRockGPUSMBus::RGBController_ASRockGPUSMBus(ASRockGPUSMBusControl
RGBController_ASRockGPUSMBus::~RGBController_ASRockGPUSMBus()
{
Shutdown();
delete controller;
}
@@ -29,6 +29,18 @@ CorsairPeripheralV2Controller::CorsairPeripheralV2Controller(hid_device* dev_han
| If the PID is in the know wireless receivers list |
| switch the write_cmd to talk to the device and retry |
\*---------------------------------------------------------*/
/*---------------------------------------------------------*\
| Establish the packet size before talking to the device. |
\*---------------------------------------------------------*/
uint16_t report_size = GetOutputReportSize();
if(report_size >= CORSAIR_V2_WRITE_SIZE && report_size <= CORSAIR_V2_PACKET_SIZE)
{
pkt_sze = report_size;
}
LOG_DEBUG("[%s] Packet length set to %d", device_name.c_str(), pkt_sze);
unsigned int pid = GetAddressRetry(0x12);
switch(pid)
@@ -64,14 +76,12 @@ CorsairPeripheralV2Controller::CorsairPeripheralV2Controller(hid_device* dev_han
| the device being set up. |
\*---------------------------------------------------------*/
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
memset(buffer, 0, pkt_sze);
buffer[1] = write_cmd;
buffer[2] = CORSAIR_V2_CMD_GET;
buffer[3] = 0x11;
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
uint16_t result = hid_read_timeout(dev, buffer, CORSAIR_V2_PACKET_SIZE, CORSAIR_V2_TIMEOUT);
result++;
pkt_sze = std::max(result, (uint16_t)CORSAIR_V2_WRITE_SIZE);
LOG_DEBUG("[%s] Packet length set to %d", device_name.c_str(), pkt_sze);
hid_write(dev, buffer, pkt_sze);
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
/*---------------------------------------------------------*\
| NB: If the device is not found in the device list |
@@ -108,22 +118,115 @@ CorsairPeripheralV2Controller::CorsairPeripheralV2Controller(hid_device* dev_han
return;
}
/*---------------------------------------------------------*\
| Check lighting control endpoints |
| If lighting control endpoint 2 is unavailable |
| then use endpoint 1. |
\*---------------------------------------------------------*/
if(light_ctrl == CORSAIR_V2_LIGHT_CTRL2)
}
/*---------------------------------------------------------*\
| Check lighting control endpoints |
| If lighting control endpoint 2 is unavailable |
| then use endpoint 1. |
\*---------------------------------------------------------*/
uint16_t CorsairPeripheralV2Controller::GetOutputReportSize()
{
unsigned char descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];
int length = hid_get_report_descriptor(dev, descriptor, sizeof(descriptor));
if(length <= 0)
{
result = StartTransaction(0);
if(result > 0)
{
light_ctrl = CORSAIR_V2_LIGHT_CTRL1;
StartTransaction(0);
}
StopTransaction(0);
LOG_DEBUG("[%s] Lighting Endpoint set to %02X", device_name.c_str(), light_ctrl);
return 0;
}
unsigned int report_size = 0;
unsigned int report_count = 0;
int index = 0;
while(index < length)
{
unsigned char item = descriptor[index];
unsigned char tag = item & 0xFC;
unsigned char item_size = item & 0x03;
/*---------------------------------------------------------*\
| A size field of 3 means four data bytes follow. |
\*---------------------------------------------------------*/
if(item_size == 3)
{
item_size = 4;
}
if(index + 1 + item_size > length)
{
break;
}
unsigned int value = 0;
for(unsigned char byte = 0; byte < item_size; byte++)
{
value |= (unsigned int)descriptor[index + 1 + byte] << (8 * byte);
}
switch(tag)
{
case 0x74: /* Report Size */
report_size = value;
break;
case 0x94: /* Report Count */
report_count = value;
break;
case 0x90: /* Output */
if(report_size > 0 && report_count > 0)
{
return (uint16_t)(((report_size * report_count) / 8) + 1);
}
break;
}
index += 1 + item_size;
}
return 0;
}
void CorsairPeripheralV2Controller::DetectLightingEndpoint()
{
/*---------------------------------------------------------*\
| A device that cannot be read from cannot be probed; those |
| carry the endpoint they need already set. |
\*---------------------------------------------------------*/
if(skip_reads)
{
return;
}
/*---------------------------------------------------------*\
| Lighting control 1 is the plain lighting resource and |
| takes colour block data. A device that answers |
| invalid or unsupported for it wants the alternate |
| lighting resource, which takes RGB triplets. |
\*---------------------------------------------------------*/
light_ctrl = CORSAIR_V2_LIGHT_CTRL1;
unsigned char result = StartTransaction(0);
/*---------------------------------------------------------*\
| A handle left open by an earlier session answers failed; |
| close it and open it again. |
\*---------------------------------------------------------*/
if(result == CORSAIR_V2_ERR_FAILED)
{
StopTransaction(0);
result = StartTransaction(0);
}
if(result == CORSAIR_V2_ERR_INVALID || result == CORSAIR_V2_ERR_NOT_SUPPORTED)
{
light_ctrl = CORSAIR_V2_LIGHT_CTRL2;
StartTransaction(0);
}
StopTransaction(0);
LOG_DEBUG("[%s] Lighting Endpoint set to %02X", device_name.c_str(), light_ctrl);
}
CorsairPeripheralV2Controller::~CorsairPeripheralV2Controller()
@@ -191,9 +294,9 @@ std::string CorsairPeripheralV2Controller::GetSerialString()
void CorsairPeripheralV2Controller::SetRenderMode(corsair_v2_device_mode mode)
{
uint8_t buffer[CORSAIR_V2_WRITE_SIZE];
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
memset(buffer, 0, CORSAIR_V2_WRITE_SIZE);
memset(buffer, 0, pkt_sze);
/*---------------------------------------------------------*\
| Set Mode |
@@ -203,19 +306,19 @@ void CorsairPeripheralV2Controller::SetRenderMode(corsair_v2_device_mode mode)
buffer[3] = CORSAIR_V2_VALUE_MODE;
buffer[5] = mode;
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
hid_write(dev, buffer, pkt_sze);
if(!skip_reads)
{
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
}
}
void CorsairPeripheralV2Controller::LightingControl(uint8_t opt1)
{
uint8_t buffer[CORSAIR_V2_WRITE_SIZE];
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
memset(buffer, 0, CORSAIR_V2_WRITE_SIZE);
memset(buffer, 0, pkt_sze);
/*---------------------------------------------------------*\
| The Corsair command is the same for each initialisation |
@@ -227,11 +330,11 @@ void CorsairPeripheralV2Controller::LightingControl(uint8_t opt1)
buffer[3] = opt1;
buffer[5] = 0x00;
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
hid_write(dev, buffer, pkt_sze);
if(!skip_reads)
{
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
}
}
@@ -270,18 +373,18 @@ unsigned int CorsairPeripheralV2Controller::GetAddressRetry(uint8_t address)
unsigned int CorsairPeripheralV2Controller::GetAddress(uint8_t address)
{
uint8_t buffer[CORSAIR_V2_WRITE_SIZE];
uint8_t read[CORSAIR_V2_WRITE_SIZE];
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
uint8_t read[CORSAIR_V2_PACKET_SIZE];
memset(buffer, 0, CORSAIR_V2_WRITE_SIZE);
memset(read, 0, CORSAIR_V2_WRITE_SIZE);
memset(buffer, 0, pkt_sze);
memset(read, 0, pkt_sze);
buffer[1] = write_cmd;
buffer[2] = CORSAIR_V2_CMD_GET;
buffer[3] = address;
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
hid_read_timeout(dev, read, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
hid_write(dev, buffer, pkt_sze);
hid_read_timeout(dev, read, pkt_sze, CORSAIR_V2_TIMEOUT);
unsigned int temp = (unsigned int)(read[6] << 24 | read[5] << 16 | read[4] << 8 | read[3]);
LOG_DEBUG("[%s] GetAddress %02X - %02X %02X - %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(),
@@ -299,20 +402,20 @@ unsigned int CorsairPeripheralV2Controller::GetAddress(uint8_t address)
unsigned char CorsairPeripheralV2Controller::StartTransaction(uint8_t opt1)
{
uint8_t buffer[CORSAIR_V2_WRITE_SIZE];
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
memset(buffer, 0, CORSAIR_V2_WRITE_SIZE);
memset(buffer, 0, pkt_sze);
buffer[1] = write_cmd;
buffer[2] = CORSAIR_V2_CMD_START_TX;
buffer[3] = opt1;
buffer[4] = light_ctrl;
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
hid_write(dev, buffer, pkt_sze);
if(!skip_reads)
{
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
}
return buffer[2];
@@ -320,20 +423,20 @@ unsigned char CorsairPeripheralV2Controller::StartTransaction(uint8_t opt1)
void CorsairPeripheralV2Controller::StopTransaction(uint8_t opt1)
{
uint8_t buffer[CORSAIR_V2_WRITE_SIZE];
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
memset(buffer, 0, CORSAIR_V2_WRITE_SIZE);
memset(buffer, 0, pkt_sze);
buffer[1] = write_cmd;
buffer[2] = CORSAIR_V2_CMD_STOP_TX;
buffer[3] = 0x01;
buffer[4] = opt1;
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
hid_write(dev, buffer, pkt_sze);
if(!skip_reads)
{
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
}
}
@@ -347,6 +450,11 @@ void CorsairPeripheralV2Controller::ClearPacketBuffer()
uint8_t result = 0;
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
/*---------------------------------------------------------*\
| Draining expects to find nothing, so the last read here |
| always waits out the timeout. Keep it short: this runs |
| before every LED update. |
\*---------------------------------------------------------*/
do
{
result = hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT);
@@ -356,6 +464,8 @@ void CorsairPeripheralV2Controller::ClearPacketBuffer()
void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size)
{
std::lock_guard<std::mutex> lock(device_mutex);
const uint8_t offset1 = 8;
const uint8_t offset2 = 4;
uint16_t remaining = data_size;
@@ -389,7 +499,7 @@ void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size)
if(!skip_reads)
{
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT);
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
}
remaining -= copy_bytes;
@@ -414,7 +524,7 @@ void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size)
if(!skip_reads)
{
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT);
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
}
remaining -= copy_bytes;
@@ -426,6 +536,8 @@ void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size)
void CorsairPeripheralV2Controller::UpdateHWMode(uint16_t mode, corsair_v2_color /*color_mode*/, uint8_t /*speed*/,
uint8_t /*direction*/, uint8_t /*brightness*/, std::vector<RGBColor> /*colors*/)
{
std::lock_guard<std::mutex> lock(device_mutex);
/*---------------------------------------------------------*\
| If we are switching to `Direct` mode |
| set device in software mode |
@@ -12,6 +12,7 @@
#pragma once
#include <string>
#include <mutex>
#include <vector>
#include <hidapi.h>
#include "LogManager.h"
@@ -21,13 +22,33 @@
#define NA 0xFFFFFFFF
#define HID_MAX_STR 255
#define CORSAIR_V2_TIMEOUT 50
/*---------------------------------------------------------*\
| A jumbo packet device answers a 1KB command in well over |
| 50ms. Missing the reply leaves it queued and the next |
| write to the device then fails outright, so this has to |
| be generous. A reply that does arrive is returned as |
| soon as it lands, so this only costs time on failure. |
\*---------------------------------------------------------*/
#define CORSAIR_V2_TIMEOUT 500
#define CORSAIR_V2_TIMEOUT_SHORT 3
#define CORSAIR_V2_VALUE_MODE 3
#define CORSAIR_V2_WRITE_WIRED_ID 8
#define CORSAIR_V2_WRITE_WIRELESS_ID 9
#define CORSAIR_V2_WRITE_SIZE 65
#define CORSAIR_V2_PACKET_SIZE 1024
/*---------------------------------------------------------*\
| Largest packet the protocol uses: 1024 payload bytes |
| plus the leading report ID. pkt_sze is measured |
| against this, and every stack buffer handed to hidapi |
| with pkt_sze is this size, so the two must agree. |
\*---------------------------------------------------------*/
#define CORSAIR_V2_PACKET_SIZE 1025
/*---------------------------------------------------------*\
| Error codes the device returns in byte 2 of a reply. |
\*---------------------------------------------------------*/
#define CORSAIR_V2_ERR_INVALID 0x01
#define CORSAIR_V2_ERR_FAILED 0x03
#define CORSAIR_V2_ERR_NOT_SUPPORTED 0x06
#define CORSAIR_V2_LIGHT_CTRL1 1
#define CORSAIR_V2_LIGHT_CTRL2 34 /* 0x22 */
@@ -113,6 +134,27 @@ protected:
std::string device_name;
uint8_t light_ctrl = CORSAIR_V2_LIGHT_CTRL2;
/*---------------------------------------------------------*\
| Works out which lighting control endpoint the device |
| accepts. Must be called only once the device has been |
| put into software render mode: endpoint 2 rejects the |
| transaction while the device is still driving its own |
| lighting, which would pick endpoint 1 for a device that |
| does not support it. |
\*---------------------------------------------------------*/
void DetectLightingEndpoint();
/*---------------------------------------------------------*\
| Size of one output report, plus the leading report ID. |
| Read from the HID report descriptor before anything is |
| sent: these devices come in a 64 byte and a 1024 byte |
| flavour, and sending a short packet to a 1024 byte |
| endpoint wedges it until the device is reset, so the |
| size has to be known before the first write rather than |
| inferred from a reply. Returns 0 if it cannot be read. |
\*---------------------------------------------------------*/
uint16_t GetOutputReportSize();
private:
void ClearPacketBuffer();
unsigned int GetAddress(uint8_t address);
@@ -122,6 +164,16 @@ private:
hid_device* dev;
/*---------------------------------------------------------*\
| Serialises device transactions. |
| The RGB controllers run a keepalive thread that writes |
| LEDs independently of the caller, and a Corsair block |
| write spans several packets. Letting two of those |
| interleave on one HID handle corrupts the transaction |
| and leaves the device unresponsive until it is reset. |
\*---------------------------------------------------------*/
std::mutex device_mutex;
uint8_t write_cmd = CORSAIR_V2_WRITE_WIRED_ID;
uint16_t pkt_sze = CORSAIR_V2_WRITE_SIZE;
bool skip_reads = false;
@@ -92,6 +92,7 @@ DetectedControllers DetectCorsairV2SoftwareControllers(hid_device_info* info, co
REGISTER_HID_DETECTOR_IP("Corsair K55 RGB PRO", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K55_RGB_PRO_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K57 RGB (Wired)", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K57_RGB_WIRED_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO SE", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_SE_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO Low Profile", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_LP_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO TKL Black", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_TKL_B_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO TKL White", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_TKL_W_PID, 1, 0xFF42);
@@ -901,7 +901,7 @@ static const corsair_v2_device k57_rgb_wired_device =
| |
| Zone "Keyboard" |
| Matrix |
| 6 Rows, 21 Columns |
| 7 Rows, 22 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone k60_rgb_pro_zone =
{
@@ -928,12 +928,34 @@ static const corsair_v2_device k60_rgb_pro_device =
&corsair_K60_layout
};
/*-------------------------------------------------------------*\
| Corsair K60 RGB Pro SE 1B1C:1B8D |
| |
| Initial implementation reuses K60 RGB Pro zone/layout |
\*-------------------------------------------------------------*/
static const corsair_v2_device k60_rgb_pro_se_device =
{
CORSAIR_K60_RGB_PRO_SE_PID,
DEVICE_TYPE_KEYBOARD,
6,
21,
{
&k60_rgb_pro_zone,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr
},
&corsair_K60_layout
};
/*-------------------------------------------------------------*\
| Corsair K60 RGB Pro Low Profile 1B1C:1BAD |
| |
| Zone "Keyboard" |
| Matrix |
| 6 Rows, 21 Columns |
| 7 Rows, 22 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone k60_rgb_pro_lp_zone =
{
@@ -966,7 +988,7 @@ static const corsair_v2_device k60_rgb_pro_lp_device =
| |
| Zone "Keyboard" |
| Matrix |
| 6 Rows, 21 Columns |
| 7 Rows, 22 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone k60_rgb_pro_tkl_zone =
{
@@ -1016,7 +1038,7 @@ static const corsair_v2_device k60_rgb_pro_tkl_device_w =
| |
| Zone "Keyboard" |
| Matrix |
| 6 Rows, 21 Columns |
| 7 Rows, 22 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone k70_core_rgb_zone =
{
@@ -1145,14 +1167,14 @@ static const corsair_v2_device k70_rgb_tkl_cs_device =
| |
| Zone "Keyboard" |
| Matrix |
| 6 Rows, 21 Columns |
| 7 Rows, 22 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone k70_rgb_pro_zone =
{
ZONE_EN_KEYBOARD,
ZONE_TYPE_MATRIX,
7,
21
22
};
static const corsair_v2_device k70_rgb_pro_device =
@@ -1160,7 +1182,7 @@ static const corsair_v2_device k70_rgb_pro_device =
CORSAIR_K70_RGB_PRO_PID,
DEVICE_TYPE_KEYBOARD,
7,
21,
22,
{
&k70_rgb_pro_zone,
nullptr,
@@ -1177,14 +1199,14 @@ static const corsair_v2_device k70_rgb_pro_device =
| |
| Zone "Keyboard" |
| Matrix |
| 6 Rows, 21 Columns |
| 7 Rows, 22 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_device k70_rgb_pro_v2_device =
{
CORSAIR_K70_RGB_PRO_V2_PID,
DEVICE_TYPE_KEYBOARD,
7,
21,
22,
{
&k70_rgb_pro_zone,
nullptr,
@@ -1193,7 +1215,8 @@ static const corsair_v2_device k70_rgb_pro_v2_device =
nullptr,
nullptr
},
&corsair_k70_pro_layout
&corsair_k70_pro_layout,
193
};
/*-------------------------------------------------------------*\
@@ -1625,6 +1648,7 @@ const corsair_v2_device* corsair_v2_device_list_data[] =
&k55_rgb_pro_device,
&k57_rgb_wired_device,
&k60_rgb_pro_device,
&k60_rgb_pro_se_device,
&k60_rgb_pro_lp_device,
&k60_rgb_pro_tkl_device_b,
&k60_rgb_pro_tkl_device_w,
@@ -63,6 +63,15 @@ typedef struct
uint8_t cols;
const corsair_v2_zone* zones[CORSAIR_ZONES_MAX];
keyboard_keymap_overlay_values* layout_new;
/*-------------------------------------------------------*\
| Number of LED slots the device expects in a direct |
| lighting write. The keymap only covers the keys that |
| exist, but the device wants its full slot count and |
| ignores a short write, so the buffer map is padded up |
| to this. Left 0 where the count is not known, which |
| keeps the keymap derived size. |
\*-------------------------------------------------------*/
uint16_t hw_led_count;
} corsair_v2_device;
/*-----------------------------------------------------*\
@@ -72,6 +81,7 @@ typedef struct
#define CORSAIR_K57_RGB_WIRED_PID 0x1B6E
#define CORSAIR_K57_RGB_WIRELESS_PID 0x1B62
#define CORSAIR_K60_RGB_PRO_PID 0x1BA0
#define CORSAIR_K60_RGB_PRO_SE_PID 0x1B8D
#define CORSAIR_K60_RGB_PRO_LP_PID 0x1BAD
#define CORSAIR_K60_RGB_PRO_TKL_B_PID 0x1BC7
#define CORSAIR_K60_RGB_PRO_TKL_W_PID 0x1BED
@@ -16,6 +16,12 @@ CorsairPeripheralV2HWController::CorsairPeripheralV2HWController(hid_device* dev
{
SetRenderMode(CORSAIR_V2_MODE_SW);
LightingControl(0x5F);
/*---------------------------------------------------------*\
| Only now that the device is in software render mode can |
| its lighting control endpoint be probed reliably. |
\*---------------------------------------------------------*/
DetectLightingEndpoint();
}
CorsairPeripheralV2HWController::~CorsairPeripheralV2HWController()
@@ -16,6 +16,12 @@ CorsairPeripheralV2SWController::CorsairPeripheralV2SWController(hid_device* dev
{
SetRenderMode(CORSAIR_V2_MODE_SW);
LightingControl(0x5F);
/*---------------------------------------------------------*\
| Only now that the device is in software render mode can |
| its lighting control endpoint be probed reliably. |
\*---------------------------------------------------------*/
DetectLightingEndpoint();
}
CorsairPeripheralV2SWController::~CorsairPeripheralV2SWController()
@@ -66,21 +66,32 @@ RGBController_CorsairV2HW::RGBController_CorsairV2HW(CorsairPeripheralV2Controll
| to not revert back into rainbow mode. Start a thread |
| to continuously send a keepalive packet every 50 sec |
\*-----------------------------------------------------*/
/*-----------------------------------------------------*\
| Start the keepalive clock before the thread runs. |
| A default constructed time_point sits at the clock |
| epoch, which is always older than the update |
| period, so the thread would otherwise fire a write |
| on its first pass - during detection, racing |
| whatever the caller is doing. |
\*-----------------------------------------------------*/
last_update_time = std::chrono::steady_clock::now();
keepalive_thread_run = true;
keepalive_thread = new std::thread(&RGBController_CorsairV2HW::KeepaliveThread, this);
}
RGBController_CorsairV2HW::~RGBController_CorsairV2HW()
{
Shutdown();
/*-----------------------------------------------------*\
| Close keepalive thread |
| Close keepalive thread first, so that it cannot issue |
| a write while the device is being shut down. |
\*-----------------------------------------------------*/
keepalive_thread_run = false;
keepalive_thread->join();
delete keepalive_thread;
Shutdown();
delete controller;
}
@@ -198,6 +209,15 @@ void RGBController_CorsairV2HW::SetupZones()
SetupColors();
/*---------------------------------------------------------*\
| Pad out to the slot count the device expects. A write |
| that is short of it is ignored by the hardware. |
\*---------------------------------------------------------*/
if(corsair->hw_led_count > max_led_value)
{
max_led_value = corsair->hw_led_count;
}
/*---------------------------------------------------------*\
| Create a buffer map of pointers which contains the |
| layout order of colors the device expects. |
@@ -53,21 +53,32 @@ RGBController_CorsairV2SW::RGBController_CorsairV2SW(CorsairPeripheralV2Controll
| to not revert back into rainbow mode. Start a thread |
| to continuously send a keepalive packet every 50 sec |
\*-----------------------------------------------------*/
/*-----------------------------------------------------*\
| Start the keepalive clock before the thread runs. |
| A default constructed time_point sits at the clock |
| epoch, which is always older than the update |
| period, so the thread would otherwise fire a write |
| on its first pass - during detection, racing |
| whatever the caller is doing. |
\*-----------------------------------------------------*/
last_update_time = std::chrono::steady_clock::now();
keepalive_thread_run = true;
keepalive_thread = new std::thread(&RGBController_CorsairV2SW::KeepaliveThread, this);
}
RGBController_CorsairV2SW::~RGBController_CorsairV2SW()
{
Shutdown();
/*-----------------------------------------------------*\
| Close keepalive thread |
| Close keepalive thread first, so that it cannot issue |
| a write while the device is being shut down. |
\*-----------------------------------------------------*/
keepalive_thread_run = false;
keepalive_thread->join();
delete keepalive_thread;
Shutdown();
delete controller;
}
@@ -185,6 +196,15 @@ void RGBController_CorsairV2SW::SetupZones()
SetupColors();
/*---------------------------------------------------------*\
| Pad out to the slot count the device expects. A write |
| that is short of it is ignored by the hardware. |
\*---------------------------------------------------------*/
if(corsair->hw_led_count > max_led_value)
{
max_led_value = corsair->hw_led_count;
}
/*---------------------------------------------------------*\
| Create a buffer map of pointers which contains the |
| layout order of colors the device expects. |
@@ -144,7 +144,8 @@ REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 GAMING OC",
REGISTER_I2C_PCI_DETECTOR("Gigabyte Geforce RTX 4070 Aero OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070_AERO_OC_12G_SUB_DEV, 0x71);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 SUPER GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070S_GAMING_OC_12G, 0x71);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 SUPER Aero OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070S_AERO_OC_12G, 0x71);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 SUPER Eagle OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070S_EAGLE_OC_ICE_12G, 0x71);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 SUPER Eagle OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070S_EAGLE_OC_12G, 0x71);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 SUPER Eagle OC ICE", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070S_EAGLE_OC_ICE_12G, 0x71);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti GAMING", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_GAMING_12G, 0x71);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_GAMING_OC_12G, 0x71);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_GAMING_OC_12G_SUB_DEV2, 0x71);
@@ -7559,7 +7559,8 @@ void LogitechHIDPP20Controller::ReadActiveProfileSector()
}
constexpr uint16_t SECTOR_SIZE = 0x63; // 99 bytes
constexpr uint16_t PAGE_SIZE = 16;
constexpr uint16_t SIZE_OF_PAGE = 16;
/*-----------------------------------------------------*\
| Step 1: load the sector into the device's read buffer |
@@ -7585,7 +7586,7 @@ void LogitechHIDPP20Controller::ReadActiveProfileSector()
\*-----------------------------------------------------*/
uint8_t sector_buf[SECTOR_SIZE] = {};
for(uint16_t offset = 0; offset < SECTOR_SIZE; offset += PAGE_SIZE)
for(uint16_t offset = 0; offset < SECTOR_SIZE; offset += SIZE_OF_PAGE)
{
uint8_t read_req[3] = {
(uint8_t)((offset >> 8) & 0xFF),
@@ -7605,9 +7606,9 @@ void LogitechHIDPP20Controller::ReadActiveProfileSector()
return;
}
size_t copy_len = (offset + PAGE_SIZE > SECTOR_SIZE)
size_t copy_len = (offset + SIZE_OF_PAGE > SECTOR_SIZE)
? (size_t)(SECTOR_SIZE - offset)
: PAGE_SIZE;
: SIZE_OF_PAGE;
memcpy(sector_buf + offset, page_resp, copy_len);
}
@@ -7617,11 +7618,11 @@ void LogitechHIDPP20Controller::ReadActiveProfileSector()
LOG_DEBUG("%s ProfileSector partition=NVS sector=1 size=%u bytes:",
LOG_TAG, (unsigned)SECTOR_SIZE);
for(uint16_t row = 0; row < SECTOR_SIZE; row += PAGE_SIZE)
for(uint16_t row = 0; row < SECTOR_SIZE; row += SIZE_OF_PAGE)
{
size_t row_len = (row + PAGE_SIZE > SECTOR_SIZE)
size_t row_len = (row + SIZE_OF_PAGE > SECTOR_SIZE)
? (size_t)(SECTOR_SIZE - row)
: PAGE_SIZE;
: SIZE_OF_PAGE;
char hex[64] = {};
char* p = hex;
@@ -423,37 +423,37 @@ MSIMotherboard185Controller::MSIMotherboard185Controller(hid_device* handle, con
}
}
/*-----------------------------------------------------*\
| If the firmware version is below the minimum, set |
| invalid version flag and return |
\*-----------------------------------------------------*/
if(version_APROM_val < board_config->minimum_firmware_version)
{
version_invalid = true;
return;
}
/*-----------------------------------------------------*\
| Fill in the number of each zone type based on the |
| board configuration |
\*-----------------------------------------------------*/
if(board_config != nullptr)
{
numof_onboard_leds = board_config->numof_onboard_leds;
numof_pipe1_leds = board_config->numof_pipe1_leds;
numof_pipe2_leds = board_config->numof_pipe2_leds;
numof_JRGBs = board_config->numof_JRGBs;
supported_zones = board_config->supported_zones;
per_led_mode = board_config->per_led_mode;
/*-------------------------------------------------*\
| If the firmware version is below the minimum, set |
| invalid version flag and return |
\*-------------------------------------------------*/
if(version_APROM_val < board_config->minimum_firmware_version)
{
version_invalid = true;
return;
}
numof_onboard_leds = board_config->numof_onboard_leds;
numof_pipe1_leds = board_config->numof_pipe1_leds;
numof_pipe2_leds = board_config->numof_pipe2_leds;
numof_JRGBs = board_config->numof_JRGBs;
supported_zones = board_config->supported_zones;
per_led_mode = board_config->per_led_mode;
}
else
{
numof_onboard_leds = 6;
numof_pipe1_leds = 1;
numof_pipe2_leds = 1;
numof_JRGBs = 2;
supported_zones = &all_zones;
per_led_mode = DIRECT_MODE_DISABLED;
numof_onboard_leds = 6;
numof_pipe1_leds = 1;
numof_pipe2_leds = 1;
numof_JRGBs = 2;
supported_zones = &all_zones;
per_led_mode = DIRECT_MODE_DISABLED;
}
/*-----------------------------------------------------*\
@@ -78,7 +78,9 @@ static const std::string board_names[] =
"MSI MAG X870E TOMAHAWK MAX WIFI PZ (MS-7E84)",
"MSI PRO B850M-A WIFI PZ (MS-7E78)",
"MSI B850MPOWER (MS-7E83)",
"MSI B840M GAMING WIFI6E (MS-7E76)"
"MSI B840M GAMING WIFI6E (MS-7E76)",
"MSI PRO B850M-A WIFI (MS-7E66)",
"MSI PRO B850-S WIFI6E (MS-7E80)"
};
static const mystic_light_761_config board_configs[] =
@@ -115,6 +117,8 @@ static const mystic_light_761_config board_configs[] =
{ &(board_names[29]), 0, 0, 0, 1, &zone_set1, MSIMotherboard761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850M-A WIFI PZ (MS-7E78)
{ &(board_names[30]), 0, 0, 0, 1, &zone_set1, MSIMotherboard761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850MPOWER (MS-7E83)
{ &(board_names[31]), 0, 0, 0, 1, &zone_set1, MSIMotherboard761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B840M GAMING WIFI6E (MS-7E76)
{ &(board_names[32]), 0, 0, 0, 1, &zone_set1, MSIMotherboard761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850M-A WIFI (MS-7E66)
{ &(board_names[33]), 0, 0, 0, 1, &zone_set1, MSIMotherboard761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850-S WIFI6E (MS-7E80)
};
enum MSI_ZONE setup_map [] =
@@ -236,6 +236,7 @@ REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E34", DetectMSIMotherboardCont
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E32", DetectMSIMotherboardControllers, MSI_USB_VID, 0x7E32, 0x0001, 0x00);
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E20", DetectMSIMotherboardControllers, MSI_USB_VID, 0x7E20, 0x0001, 0x00);
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E76", DetectMSIMotherboardControllers, MSI_USB_VID, 0x7E76, 0x0001, 0x00);
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E66", DetectMSIMotherboardControllers, MSI_USB_VID, 0x7E66, 0x0001, 0x00);
// Detector for the set of common boards
REGISTER_HID_DETECTOR_PU("MSI Mystic Light Common", DetectMSIMotherboardControllers, MSI_USB_VID_COMMON, MSI_USB_PID_COMMON, 0x0001, 0x00);
REGISTER_HID_DETECTOR_PU("MSI Mystic Light X870", DetectMSIMotherboardControllers, MSI_USB_VID_COMMON, MSI_USB_PID_COMMON, 0xFF00, 0x01);
@@ -47,9 +47,10 @@ DetectedControllers DetectPNYARGBEpicXGPULargeControllers(i2c_smbus_interface* b
return(detected_controllers);
}
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5060Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, PNY_SUB_VEN, PNY_RTX_5060TI_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070 ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, PNY_SUB_VEN, PNY_RTX_5070_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070Ti ARGB Epic-X", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PNY_SUB_VEN, PNY_RTX_5070TI_ARGB_EPIC_X_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PNY_SUB_VEN, PNY_RTX_5070TI_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5080 ARGB Epic-X OC", DetectPNYARGBEpicXGPULargeControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, PNY_SUB_VEN, PNY_RTX_5080_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5090 ARGB Epic-X OC", DetectPNYARGBEpicXGPULargeControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, PNY_SUB_VEN, PNY_RTX_5090_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5060Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, PNY_SUB_VEN, PNY_RTX_5060TI_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070 ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, PNY_SUB_VEN, PNY_RTX_5070_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070Ti ARGB Epic-X", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PNY_SUB_VEN, PNY_RTX_5070TI_ARGB_EPIC_X_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PNY_SUB_VEN, PNY_RTX_5070TI_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PNY_SUB_VEN, PNY_RTX_5070TI_ARGB_EPIC_X_OC_SUB_DEV2, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5080 ARGB Epic-X OC", DetectPNYARGBEpicXGPULargeControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, PNY_SUB_VEN, PNY_RTX_5080_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5090 ARGB Epic-X OC", DetectPNYARGBEpicXGPULargeControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, PNY_SUB_VEN, PNY_RTX_5090_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
@@ -26,11 +26,43 @@ bool TestForPalitGPUv1Controller(i2c_smbus_interface* bus, uint8_t i2c_addr)
{
bool pass = false;
switch(i2c_addr)
if(bus->info.port_id == 1)
{
case 0x08:
pass = bus->i2c_smbus_write_quick(i2c_addr, I2C_SMBUS_WRITE);
break;
/*-------------------------------------------------*\
| Check for PALIT string, which is known to work |
| for Palit variants |
\*-------------------------------------------------*/
const uint8_t palit[] = {'P', 'A', 'L', 'I', 'T'};
bool match = true;
for(size_t i = 0; i < sizeof(palit); i++)
{
int32_t letter = bus->i2c_smbus_read_byte_data(i2c_addr, 0x07 + (u8)i);
if(palit[i] != letter)
{
match = false;
}
}
/*-------------------------------------------------*\
| If PALIT string fails, try a quick write to the |
| target address, which is known to work for |
| Gainward variants |
\*-------------------------------------------------*/
if(match)
{
pass = true;
}
else
{
switch(i2c_addr)
{
case 0x08:
pass = bus->i2c_smbus_write_quick(i2c_addr, I2C_SMBUS_WRITE);
break;
}
}
}
return(pass);
@@ -40,13 +72,16 @@ bool TestForPalitGPUv2Controller(i2c_smbus_interface* bus, uint8_t i2c_addr)
{
bool pass = false;
switch(i2c_addr)
if(bus->info.port_id == 1)
{
case 0x49:
s32 data = bus->i2c_smbus_read_byte_data(i2c_addr, 0x0);
s32 mode_data = bus->i2c_smbus_read_byte_data(i2c_addr, 0xe0);
pass = (data == 0x0) && (mode_data < 0x5);
break;
switch(i2c_addr)
{
case 0x49:
s32 data = bus->i2c_smbus_read_byte_data(i2c_addr, 0x0);
s32 mode_data = bus->i2c_smbus_read_byte_data(i2c_addr, 0xe0);
pass = (data == 0x0) && (mode_data < 0x5);
break;
}
}
return(pass);
@@ -127,6 +127,8 @@ RazerController::RazerController(hid_device* dev_handle, hid_device* dev_argb_ha
case RAZER_NAGA_LEFT_HANDED_PID:
case RAZER_NAGA_PRO_V2_WIRED_PID:
case RAZER_NAGA_PRO_V2_WIRELESS_PID:
case RAZER_NAGA_PRO_V3_WIRED_PID:
case RAZER_NAGA_PRO_V3_WIRELESS_PID:
case RAZER_O11_DYNAMIC_PID:
case RAZER_STRIDER_CHROMA_PID:
case RAZER_TARTARUS_PRO_PID:
@@ -366,6 +366,8 @@ REGISTER_HID_DETECTOR_IPU("Razer Naga Pro (Wired)", Det
REGISTER_HID_DETECTOR_IPU("Razer Naga Pro (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_NAGA_PRO_WIRELESS_PID, 0x00, 0x01, 0x02);
REGISTER_HID_DETECTOR_IPU("Razer Naga Pro V2 (Wired)", DetectRazerControllers, RAZER_VID, RAZER_NAGA_PRO_V2_WIRED_PID, 0x00, 0x01, 0x02);
REGISTER_HID_DETECTOR_IPU("Razer Naga Pro V2 (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_NAGA_PRO_V2_WIRELESS_PID, 0x00, 0x01, 0x02);
REGISTER_HID_DETECTOR_PU("Razer Naga Pro V3 (Wired)", DetectRazerControllers, RAZER_VID, RAZER_NAGA_PRO_V3_WIRED_PID, 0x01, 0x03);
REGISTER_HID_DETECTOR_PU("Razer Naga Pro V3 (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_NAGA_PRO_V3_WIRELESS_PID, 0x01, 0x03);
REGISTER_HID_DETECTOR_IPU("Razer Viper", DetectRazerControllers, RAZER_VID, RAZER_VIPER_PID, 0x00, 0x01, 0x02);
REGISTER_HID_DETECTOR_IPU("Razer Viper 8kHz", DetectRazerControllers, RAZER_VID, RAZER_VIPER_8KHZ_PID, 0x00, 0x01, 0x02);
REGISTER_HID_DETECTOR_IPU("Razer Viper Mini", DetectRazerControllers, RAZER_VID, RAZER_VIPER_MINI_PID, 0x00, 0x01, 0x02);
@@ -7222,6 +7222,81 @@ static const razer_device naga_pro_v2_wireless_device =
NULL
};
/*-------------------------------------------------------------*\
| Razer Naga Pro V3 1532:00E7 (wired) 1532:00E8 (wireless) |
| |
| Zone "Logo" |
| Single |
| 1 LED |
| |
| Zone "Numpad" |
| Single |
| 1 LED |
\*-------------------------------------------------------------*/
static const razer_zone naga_pro_v3_scroll_wheel_zone =
{
"Scroll Wheel",
ZONE_TYPE_SINGLE,
1,
1
};
static const razer_zone naga_pro_v3_logo_zone =
{
"Logo",
ZONE_TYPE_SINGLE,
1,
1
};
static const razer_zone naga_pro_v3_numpad_zone =
{
"Numpad",
ZONE_TYPE_SINGLE,
1,
1
};
static const razer_device naga_pro_v3_wired_device =
{
"Razer Naga Pro V3 (Wired)",
RAZER_NAGA_PRO_V3_WIRED_PID,
DEVICE_TYPE_MOUSE,
RAZER_MATRIX_TYPE_EXTENDED,
0x1F,
1,
3,
{
&naga_pro_v3_logo_zone,
&naga_pro_v3_scroll_wheel_zone,
&naga_pro_v3_numpad_zone,
NULL,
NULL,
NULL
},
NULL
};
static const razer_device naga_pro_v3_wireless_device =
{
"Razer Naga Pro V3 (Wireless)",
RAZER_NAGA_PRO_V3_WIRELESS_PID,
DEVICE_TYPE_MOUSE,
RAZER_MATRIX_TYPE_EXTENDED,
0x1F,
1,
3,
{
&naga_pro_v3_logo_zone,
&naga_pro_v3_scroll_wheel_zone,
&naga_pro_v3_numpad_zone,
NULL,
NULL,
NULL
},
NULL
};
/*-------------------------------------------------------------*\
| Razer Viper 8kHz 1532:0091 |
| |
@@ -9590,6 +9665,8 @@ const razer_device* razer_device_list[] =
&naga_pro_wireless_device,
&naga_pro_v2_wired_device,
&naga_pro_v2_wireless_device,
&naga_pro_v3_wired_device,
&naga_pro_v3_wireless_device,
&viper_8khz_device,
&viper_mini_device,
&viper_ultimate_wired_device,
@@ -213,6 +213,8 @@
#define RAZER_NAGA_PRO_WIRELESS_PID 0x0090
#define RAZER_NAGA_PRO_V2_WIRED_PID 0x00A7
#define RAZER_NAGA_PRO_V2_WIRELESS_PID 0x00A8
#define RAZER_NAGA_PRO_V3_WIRED_PID 0x00E7
#define RAZER_NAGA_PRO_V3_WIRELESS_PID 0x00E8
#define RAZER_OROCHI_2011_PID 0x0013
#define RAZER_OROCHI_2013_PID 0x0039
#define RAZER_OROCHI_CHROMA_PID 0x0048
+5 -4
View File
@@ -3793,19 +3793,20 @@ void NetworkServer::SendReply_ServerString(NetworkClientInfo* client_info)
void NetworkServer::SendReply_PluginList(NetworkClientInfo* client_info)
{
unsigned int data_size = 0;
unsigned int data_ptr = 0;
unsigned int data_ptr = 0;
unsigned int data_size = 0;
unsigned short num_plugins = 0;
/*-----------------------------------------------------*\
| Calculate data size |
\*-----------------------------------------------------*/
unsigned short num_plugins = (unsigned short)plugin_manager->GetPluginCount();
data_size += sizeof(data_size);
data_size += sizeof(num_plugins);
if(plugin_manager)
{
num_plugins = (unsigned short)plugin_manager->GetPluginCount();
for(unsigned int i = 0; i < num_plugins; i++)
{
data_size += sizeof(unsigned short) * 3;
+38 -5
View File
@@ -163,6 +163,21 @@ ProfileManager::~ProfileManager()
}
void ProfileManager::ApplyActiveProfilePluginData()
{
/*-----------------------------------------------------*\
| The server only sends profile data to the clients |
| connected at the time it loads, and plugins only |
| exist in the GUI, so read it back here |
\*-----------------------------------------------------*/
if(!active_profile.empty() && ResourceManager::get()->IsLocalClient() && (ResourceManager::get()->GetLocalClient()->GetSupportsProfileManagerAPI()))
{
LOG_DEBUG("[%s] Reading active profile for plugin data: %s", PROFILEMANAGER, active_profile.c_str());
OnProfileLoaded(ResourceManager::get()->GetLocalClient()->ProfileManager_DownloadProfile(active_profile));
}
}
void ProfileManager::ClearActiveProfile()
{
/*-------------------------------------------------*\
@@ -903,6 +918,8 @@ bool ProfileManager::SaveConfiguration()
\*-----------------------------------------------------*/
std::vector<RGBController *> controllers = ResourceManager::get()->GetRGBControllers();
configuration_save_mutex.lock();
/*-----------------------------------------------------*\
| Open an output file in the profile directory |
\*-----------------------------------------------------*/
@@ -1005,6 +1022,8 @@ bool ProfileManager::SaveConfiguration()
\*-----------------------------------------------------*/
controller_file.close();
configuration_save_mutex.unlock();
/*-----------------------------------------------------*\
| Reinitialize manually configured controllers list |
\*-----------------------------------------------------*/
@@ -1274,6 +1293,8 @@ bool ProfileManager::LoadAutoProfile(std::string setting_name)
\*-----------------------------------------------------*/
if(!profile_name.empty())
{
LOG_INFO("[%s] Loading %s: %s", PROFILEMANAGER, setting_name.c_str(), profile_name.c_str());
return(LoadProfile(profile_name));
}
else
@@ -1548,6 +1569,23 @@ bool ProfileManager::LoadProfileWithOptions
bool load_state
)
{
/*-------------------------------------------------*\
| Get JSON data for given profile name |
\*-------------------------------------------------*/
nlohmann::json profile_json = ReadProfileJSON(profile_name);
/*-------------------------------------------------*\
| Stop here if the profile could not be read. |
| Carrying on would clear the active profile and |
| send an empty profile to connected clients. |
\*-------------------------------------------------*/
if(profile_json.empty())
{
LOG_WARNING("[%s] Profile not found or empty, nothing to load: %s", PROFILEMANAGER, profile_name.c_str());
return(false);
}
/*-------------------------------------------------*\
| Clear stored active profile data |
\*-------------------------------------------------*/
@@ -1562,11 +1600,6 @@ bool ProfileManager::LoadProfileWithOptions
delete active_rgb_controllers_copy[controller_idx];
}
/*-------------------------------------------------*\
| Get JSON data for given profile name |
\*-------------------------------------------------*/
nlohmann::json profile_json = ReadProfileJSON(profile_name);
/*-------------------------------------------------*\
| Load the controller state data for this profile |
| into the active profile data |
+4
View File
@@ -78,6 +78,8 @@ public:
ProfileManager(const filesystem::path& config_dir);
~ProfileManager();
void ApplyActiveProfilePluginData();
void ClearActiveProfile();
void DeleteProfile(std::string profile_name);
@@ -156,6 +158,8 @@ private:
filesystem::path configuration_directory;
filesystem::path profile_directory;
std::mutex configuration_save_mutex;
/*-----------------------------------------------------*\
| ProfileManager Callbacks |
\*-----------------------------------------------------*/
+6
View File
@@ -280,6 +280,12 @@ ResourceManager::ResourceManager()
logmanager_settings_schema["file_count_limit"]["default"] = 10;
logmanager_settings_schema["file_count_limit"]["minimum"] = 0;
logmanager_settings_schema["dialog_no_show_hashes"]["title"] = QT_TRANSLATE_NOOP("Settings", "Dialog No-Show Hashes");
logmanager_settings_schema["dialog_no_show_hashes"]["type"] = "array";
logmanager_settings_schema["dialog_no_show_hashes"]["description"] = QT_TRANSLATE_NOOP("Settings", "A list of hashes of dialog text that should not be shown. Selecting \"Do Not Show Again\" on a dialog adds it to this list.");
logmanager_settings_schema["dialog_no_show_hashes"]["items"]["type"] = "integer";
logmanager_settings_schema["dialog_no_show_hashes"]["visible"] = false;
settings_manager->RegisterSettingsSchema("LogManager", QT_TRANSLATE_NOOP("Settings", "Log Manager"), logmanager_settings_schema);
/*-----------------------------------------------------*\
+31 -11
View File
@@ -175,18 +175,38 @@ bool i2c_smbus_linux_detect()
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)
/*-----------------------------------------------------*\
| Walk up sysfs to the nearest PCI device. |
| Some I2C adapters sit below an intermediate device. |
\*-----------------------------------------------------*/
while(true)
{
char pci_vendor_path[PATH_MAX];
char pci_device_path[PATH_MAX];
snprintf(pci_vendor_path, sizeof(pci_vendor_path), "%s/vendor", path);
snprintf(pci_device_path, sizeof(pci_device_path), "%s/device", path);
if(access(pci_vendor_path, R_OK) == 0 && access(pci_device_path, R_OK) == 0)
{
break;
}
char* last_slash = strrchr(path, '/');
if(last_slash == NULL || last_slash == path)
{
path[0] = '\0';
break;
}
*last_slash = '\0';
}
if(path[0] == '\0')
{
continue;
*last_slash = '\0';
}
}
else
{
+50
View File
@@ -0,0 +1,50 @@
/*---------------------------------------------------------*\
| i2c_intel_gpu.h |
| |
| Bits specific to Intel GPUs to reliably detect |
| the I2C bus that has RGB control |
| |
| Jan Dvořák 13 Sep 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <cstring>
#include "i2c_smbus.h"
#include "pci_ids.h"
inline constexpr const char * RECOGNIZED_INTEL_I2C_BUS_NAMES[] =
{
/*-----------------------------------------------------*\
| Linux Intel discrete GPU internal I2C controller |
\*-----------------------------------------------------*/
"Synopsys DesignWare I2C adapter",
nullptr
};
inline bool is_intel_gpu_i2c_bus(const i2c_smbus_interface *bus)
{
if(bus->info.pci_vendor != INTEL_VEN)
{
return false;
}
const char *name;
size_t idx = 0;
while((name = RECOGNIZED_INTEL_I2C_BUS_NAMES[idx++]) != nullptr)
{
const char *pos = std::strstr(bus->info.device_name, name);
if(pos == bus->info.device_name)
{
return true;
}
}
return false;
}
+2
View File
@@ -707,6 +707,7 @@
#define GIGABYTE_RTX4070_AERO_OC_12G_SUB_DEV 0x40E6
#define GIGABYTE_RTX4070S_GAMING_OC_12G 0x4138
#define GIGABYTE_RTX4070S_AERO_OC_12G 0x4139
#define GIGABYTE_RTX4070S_EAGLE_OC_12G 0x413A
#define GIGABYTE_RTX4070S_EAGLE_OC_ICE_12G 0x4148
#define GIGABYTE_RTX4070TI_GAMING_12G 0x40DF
#define GIGABYTE_RTX4070TI_GAMING_OC_12G 0x40C6
@@ -912,6 +913,7 @@
#define PNY_RTX_5070_ARGB_EPIC_X_OC_SUB_DEV 0x1439
#define PNY_RTX_5070TI_ARGB_EPIC_X_SUB_DEV 0x143B
#define PNY_RTX_5070TI_ARGB_EPIC_X_OC_SUB_DEV 0x143A
#define PNY_RTX_5070TI_ARGB_EPIC_X_OC_SUB_DEV2 0x144A
#define PNY_RTX_5060TI_ARGB_EPIC_X_OC_SUB_DEV 0x143E
#define PNY_RTX_5080_ARGB_EPIC_X_OC_SUB_DEV 0x1435
#define PNY_RTX_5090_ARGB_EPIC_X_OC_SUB_DEV 0x1446
@@ -9,6 +9,7 @@
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "JsonUtils.h"
#include "OpenRGBDynamicSettingsWidget.h"
#include "OpenRGBDeviceEditorDialog.h"
#include "ProfileManager.h"
@@ -125,6 +126,11 @@ OpenRGBDeviceEditorDialog::OpenRGBDeviceEditorDialog(RGBController *dev, QWidget
\*-----------------------------------------------------*/
for(std::size_t configuration_idx = 0; configuration_idx < configuration_entries.size(); configuration_idx++)
{
if(JsonUtils::JsonGetBool(configuration_entries[configuration_idx].value, "visible", true) == false)
{
continue;
}
OpenRGBDynamicSettingsWidget* item_widget = new OpenRGBDynamicSettingsWidget(configuration_entries[configuration_idx].key, configuration_entries[configuration_idx].value, configuration_value);
item_widget->SetCallback(Callback, this);
+16 -5
View File
@@ -1629,11 +1629,6 @@ void OpenRGBDevicePage::UpdateModeUi()
ui->PerLEDCheck->setEnabled(enable_controls);
ui->PerLEDCheck->setChecked(per_led);
ui->PerLEDCheck->blockSignals(false);
if(DeviceViewShowing)
{
ui->DeviceViewBoxFrame->show();
}
}
else
{
@@ -1647,6 +1642,22 @@ void OpenRGBDevicePage::UpdateModeUi()
ui->DeviceViewBoxFrame->hide();
}
/*-----------------------------------------------------*\
| Show the device view if the selected mode is a per- |
| LED mode or if the selected zone mode is following |
| entire device and the device mode is per-LED. |
\*-----------------------------------------------------*/
if((per_led)
|| ((selected_zone_mode)
&& (selected_mode == -1)
&& (device->GetModeColorMode(device->GetActiveMode()) == MODE_COLORS_PER_LED)))
{
if(DeviceViewShowing)
{
ui->DeviceViewBoxFrame->show();
}
}
if(supports_mode_specific)
{
ui->ModeSpecificCheck->blockSignals(true);
+190 -18
View File
@@ -54,8 +54,7 @@ static void OpenRGBDialogLogManagerCallback(void * this_ptr, unsigned int update
switch(update_reason)
{
case LOGMANAGER_UPDATE_REASON_SHOW_DIALOG:
this_obj->SetDialogMessage(message);
QMetaObject::invokeMethod(this_obj, "onShowDialogMessage", Qt::QueuedConnection);
QMetaObject::invokeMethod(this_obj, "onShowDialogMessage", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(message->text)));
break;
}
}
@@ -133,6 +132,11 @@ static void DeletePluginCallback(void * this_ptr, OpenRGBPluginEntry* plugin)
this_obj->RemovePlugin(plugin);
}
static bool AutoProfileIsSet(json& profilemanager_settings, std::string key)
{
return(profilemanager_settings.contains(key) && (JsonUtils::JsonGetString(profilemanager_settings[key], "name") != ""));
}
bool OpenRGBDialog::IsMinimizeOnClose()
{
json ui_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("UserInterface");
@@ -279,6 +283,7 @@ OpenRGBDialog::OpenRGBDialog(QWidget *parent) : QMainWindow(parent), ui(new Ui::
#if defined(_WIN32)
drivers_settings_schema["shared_smbus_acces"]["title"] = QT_TRANSLATE_NOOP("Settings", "Shared SMBus Access (restart required)");
drivers_settings_schema["shared_smbus_acces"]["type"] = "bool";
drivers_settings_schema["shared_smbus_access"]["default"] = true;
drivers_settings_schema["smbus_sleep_mode"]["title"] = QT_TRANSLATE_NOOP("Settings", "SMBus Sleep Mode (restart required)");
drivers_settings_schema["smbus_sleep_mode"]["type"] = "integer";
@@ -288,6 +293,7 @@ OpenRGBDialog::OpenRGBDialog(QWidget *parent) : QMainWindow(parent), ui(new Ui::
drivers_settings_schema["smbus_sleep_mode"]["enumNames"][1] = "Short Busy";
drivers_settings_schema["smbus_sleep_mode"]["enum"][2] = 2;
drivers_settings_schema["smbus_sleep_mode"]["enumNames"][2] = "Always Sleep";
drivers_settings_schema["smbus_sleep_mode"]["default"] = 2;
#else
drivers_settings_schema["amd_smbus_reduce_cpu"]["title"] = QT_TRANSLATE_NOOP("Settings", "AMD SMBus: Reduce CPU Usage (restart required)");
drivers_settings_schema["amd_smbus_reduce_cpu"]["type"] = "bool";
@@ -296,6 +302,11 @@ OpenRGBDialog::OpenRGBDialog(QWidget *parent) : QMainWindow(parent), ui(new Ui::
ResourceManager::get()->GetSettingsManager()->RegisterSettingsSchema("Drivers", QT_TRANSLATE_NOOP("Settings", "Drivers"), drivers_settings_schema);
#endif
/*-----------------------------------------------------*\
| Migrate settings from pre-1.0 versions |
\*-----------------------------------------------------*/
MigrateLegacySettings();
/*-----------------------------------------------------*\
| Register resource manager callbacks |
\*-----------------------------------------------------*/
@@ -1403,11 +1414,6 @@ void OpenRGBDialog::UpdateDevicesList()
}
}
void OpenRGBDialog::SetDialogMessage(PLogMessage msg)
{
dialog_message = QString::fromStdString(msg->text);
}
void OpenRGBDialog::SetLanguage(std::string locale)
{
QApplication* app = static_cast<QApplication *>(QApplication::instance());
@@ -1627,10 +1633,13 @@ void OpenRGBDialog::onDetectionEnded()
| Load plugins after the first detection (ONLY the |
| first) |
\*-----------------------------------------------------*/
bool plugins_just_loaded = false;
if(!plugins_loaded)
{
plugin_manager->ScanAndLoadPlugins();
plugins_loaded = true;
plugins_just_loaded = true;
PluginsPage->RefreshList();
}
@@ -1642,7 +1651,173 @@ void OpenRGBDialog::onDetectionEnded()
/*-----------------------------------------------------*\
| Load the on open automatic profile |
\*-----------------------------------------------------*/
ResourceManager::get()->GetProfileManager()->LoadAutoProfileOpen();
bool open_profile_loaded = ResourceManager::get()->GetProfileManager()->LoadAutoProfileOpen();
/*-----------------------------------------------------*\
| With no profile to open, the plugins that were just |
| loaded still need the active profile's plugin data |
\*-----------------------------------------------------*/
if(plugins_just_loaded && !open_profile_loaded)
{
ResourceManager::get()->GetProfileManager()->ApplyActiveProfilePluginData();
}
}
void OpenRGBDialog::MigrateLegacySettings()
{
/*-----------------------------------------------------*\
| Legacy AutoStart argument flags and value keys |
\*-----------------------------------------------------*/
static const char* legacy_arguments[][3] =
{
{ "setserver", "--server", "" },
{ "setserverhost", "--server-host", "host" },
{ "setserverport", "--server-port", "port" },
{ "setclient", "--client", "client" },
{ "setcustom", "", "custom" },
};
static const char* legacy_profiles[] =
{
"exit_profile",
"resume_profile",
"suspend_profile",
};
SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager();
json autostart_settings = settings_manager->GetSettings("AutoStart");
json ui_settings = settings_manager->GetSettings("UserInterface");
json profilemanager_settings = settings_manager->GetSettings("ProfileManager");
bool settings_migrated = false;
bool ui_migrated = false;
bool profiles_migrated = false;
/*-----------------------------------------------------*\
| Migrate pre-1.0 AutoStart keys |
\*-----------------------------------------------------*/
if(autostart_settings.contains("setminimized"))
{
std::string arguments;
std::string profile_name = JsonUtils::JsonGetString(autostart_settings, "profile");
for(unsigned int arg_idx = 0; arg_idx < (sizeof(legacy_arguments) / sizeof(legacy_arguments[0])); arg_idx++)
{
if(JsonUtils::JsonGetBool(autostart_settings, legacy_arguments[arg_idx][0]))
{
std::string argument = legacy_arguments[arg_idx][1];
std::string value = JsonUtils::JsonGetString(autostart_settings, legacy_arguments[arg_idx][2]);
if(value != "")
{
if(argument != "")
{
argument += " ";
}
argument += value;
}
if(argument != "")
{
if(arguments != "")
{
arguments += " ";
}
arguments += argument;
}
}
autostart_settings.erase(legacy_arguments[arg_idx][0]);
autostart_settings.erase(legacy_arguments[arg_idx][2]);
}
if(!autostart_settings.contains("start_minimized"))
{
autostart_settings["start_minimized"] = JsonUtils::JsonGetBool(autostart_settings, "setminimized");
}
if(!autostart_settings.contains("custom_arguments"))
{
autostart_settings["custom_arguments"] = arguments;
}
if(JsonUtils::JsonGetBool(autostart_settings, "setprofile") && (profile_name != ""))
{
if(!AutoProfileIsSet(profilemanager_settings, "open_profile"))
{
profilemanager_settings["open_profile"]["enabled"] = true;
profilemanager_settings["open_profile"]["name"] = profile_name;
profiles_migrated = true;
}
if(!AutoProfileIsSet(profilemanager_settings, "service_startup_profile"))
{
profilemanager_settings["service_startup_profile"]["enabled"] = true;
profilemanager_settings["service_startup_profile"]["name"] = profile_name;
profiles_migrated = true;
}
}
autostart_settings.erase("setminimized");
autostart_settings.erase("setprofile");
autostart_settings.erase("profile");
settings_manager->SetSettings("AutoStart", autostart_settings);
settings_migrated = true;
}
/*-----------------------------------------------------*\
| Migrate pre-1.0 tray icon and autoload profile keys |
\*-----------------------------------------------------*/
if(ui_settings.contains("greyscale_tray_icon"))
{
if(!ui_settings.contains("monochrome_tray_icon"))
{
ui_settings["monochrome_tray_icon"] = JsonUtils::JsonGetBool(ui_settings, "greyscale_tray_icon");
}
ui_settings.erase("greyscale_tray_icon");
ui_migrated = true;
}
if(ui_settings.contains("autoload_profiles"))
{
for(unsigned int profile_idx = 0; profile_idx < (sizeof(legacy_profiles) / sizeof(legacy_profiles[0])); profile_idx++)
{
std::string key = legacy_profiles[profile_idx];
if(ui_settings["autoload_profiles"].contains(key) && !AutoProfileIsSet(profilemanager_settings, key))
{
json legacy_profile = ui_settings["autoload_profiles"][key];
profilemanager_settings[key]["enabled"] = JsonUtils::JsonGetBool(legacy_profile, "enabled");
profilemanager_settings[key]["name"] = JsonUtils::JsonGetString(legacy_profile, "name");
profiles_migrated = true;
}
}
ui_settings.erase("autoload_profiles");
ui_migrated = true;
}
if(ui_migrated)
{
settings_manager->SetSettings("UserInterface", ui_settings);
settings_migrated = true;
}
if(settings_migrated)
{
if(profiles_migrated)
{
settings_manager->SetSettings("ProfileManager", profilemanager_settings);
}
settings_manager->SaveSettings();
LOG_INFO("[OpenRGBDialog] Migrated legacy AutoStart and profile settings");
}
}
void OpenRGBDialog::onSettingsUpdated()
@@ -1819,9 +1994,9 @@ void OpenRGBDialog::on_ShowHide()
}
}
void OpenRGBDialog::onShowDialogMessage()
void OpenRGBDialog::onShowDialogMessage(QString message)
{
std::size_t hash = std::hash<std::string>{}(dialog_message.toStdString());
std::size_t hash = std::hash<std::string>{}(message.toStdString());
/*-----------------------------------------------------*\
| Load the LogManager settings and check if the hash of |
@@ -1843,7 +2018,6 @@ void OpenRGBDialog::onShowDialogMessage()
{
if(log_manager_settings["dialog_no_show_hashes"][list_idx] == hash)
{
dialog_message.clear();
return;
}
}
@@ -1851,21 +2025,21 @@ void OpenRGBDialog::onShowDialogMessage()
QMessageBox box;
box.setInformativeText(QCoreApplication::translate("ResourceManager", dialog_message.toUtf8()));
box.setInformativeText(QCoreApplication::translate("ResourceManager", message.toUtf8()));
QCheckBox* CheckBox_DontShowAgain = new QCheckBox("Don't show this message again");
DontShowAgain = false;
bool DontShowAgain = false;
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
QObject::connect(CheckBox_DontShowAgain, &QCheckBox::checkStateChanged, [this](Qt::CheckState state)
QObject::connect(CheckBox_DontShowAgain, &QCheckBox::checkStateChanged, [&DontShowAgain](Qt::CheckState state)
#else
QObject::connect(CheckBox_DontShowAgain, &QCheckBox::stateChanged, [this](int state)
QObject::connect(CheckBox_DontShowAgain, &QCheckBox::stateChanged, [&DontShowAgain](int state)
#endif
{
if(static_cast<Qt::CheckState>(state) == Qt::CheckState::Checked)
{
this->DontShowAgain = true;
DontShowAgain = true;
}
});
@@ -1888,8 +2062,6 @@ void OpenRGBDialog::onShowDialogMessage()
}
DontShowAgain = false;
dialog_message.clear();
}
void OpenRGBDialog::on_ReShow(QSystemTrayIcon::ActivationReason reason)
+2 -6
View File
@@ -59,11 +59,8 @@ public:
static bool IsMinimizeOnClose();
void SetDialogMessage(PLogMessage msg);
void SetLanguage(std::string locale);
bool DontShowAgain;
signals:
void ProfileListChanged();
@@ -126,8 +123,6 @@ private:
QAction * trayActionRescan;
QAction * trayActionExit;
QString dialog_message;
/*-----------------------------------------------------*\
| User interface |
\*-----------------------------------------------------*/
@@ -151,6 +146,7 @@ private:
void ShowHideDevicesList();
void closeEvent(QCloseEvent *event) override;
bool SelectConfigProfile(const std::string name);
void MigrateLegacySettings();
void SetDetectionViewState(bool detection_showing);
void SaveProfile();
@@ -185,7 +181,7 @@ private slots:
void onSettingsUpdated();
void on_SetAllDevices(unsigned char red, unsigned char green, unsigned char blue);
void on_ShowHide();
void onShowDialogMessage();
void onShowDialogMessage(QString message);
void on_ReShow(QSystemTrayIcon::ActivationReason reason);
void on_ProfileSelected();
void on_ProfileBox_currentIndexChanged(int index);
@@ -146,6 +146,11 @@ OpenRGBDynamicSettingsWidget::OpenRGBDynamicSettingsWidget(std::string key, nloh
\*---------------------------------------------*/
for(std::size_t setting_idx = 0; setting_idx < setting_entries.size(); setting_idx++)
{
if(JsonUtils::JsonGetBool(setting_entries[setting_idx].value, "visible", true) == false)
{
continue;
}
OpenRGBDynamicSettingsWidget* item_widget = new OpenRGBDynamicSettingsWidget(setting_entries[setting_idx].key, setting_entries[setting_idx].value, settings_json);
item_widget->SetCallback(&OpenRGBDynamicSettingsWidget::NestedCallback, this);
@@ -13,6 +13,7 @@
#include <QLayoutItem>
#include <QSpacerItem>
#include <QVBoxLayout>
#include "JsonUtils.h"
#include "OpenRGBDynamicSettingsWidget.h"
#include "OpenRGBSettingsPage.h"
#include "ResourceManager.h"
@@ -139,6 +140,11 @@ void OpenRGBSettingsPage::UpdateInterface()
\*-----------------------------------------------------*/
for(std::size_t setting_idx = 0; setting_idx < setting_entries.size(); setting_idx++)
{
if(JsonUtils::JsonGetBool(setting_entries[setting_idx].value, "visible", true) == false)
{
continue;
}
nlohmann::json setting_value;
setting_value[setting_entries[setting_idx].key] = ResourceManager::get()->GetSettingsManager()->GetSettings(setting_entries[setting_idx].key);
OpenRGBDynamicSettingsWidget* item_widget = new OpenRGBDynamicSettingsWidget(setting_entries[setting_idx].key, setting_entries[setting_idx].value, setting_value);