Compare commits

..
82 changed files with 1041 additions and 5735 deletions
-15
View File
@@ -119,21 +119,6 @@ bool AutoStart::IsAutoStartEnabled()
std::string AutoStart::GetExePath()
{
/*-----------------------------------------------------*\
| When running from an AppImage, the AppImage runtime |
| sets the APPIMAGE environment variable to the path of |
| the AppImage file itself. /proc/self/exe would |
| instead point at the extracted binary inside the |
| temporary squashfs mount, which disappears once the |
| AppImage is closed, so prefer APPIMAGE when it is set |
\*-----------------------------------------------------*/
const char* appimage_path = getenv("APPIMAGE");
if(appimage_path != NULL)
{
return(std::string(appimage_path));
}
/*-----------------------------------------------------*\
| Create the OpenRGB executable path |
\*-----------------------------------------------------*/
@@ -1,199 +0,0 @@
/*---------------------------------------------------------*\
| ASRockGPUSMBusController.cpp |
| |
| Driver for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "ASRockGPUSMBusController.h"
#include "LogManager.h"
#include <chrono>
#include <cstdio>
#include <cstring>
#include <thread>
using namespace std::chrono_literals;
ASRockGPUSMBusController::ASRockGPUSMBusController(i2c_smbus_interface* bus, asrock_gpu_dev_id dev, std::string name)
{
this->bus = bus;
this->dev = dev;
this->device_name = name;
this->num_channels = 0;
memset(enabled_channels, 0, sizeof(enabled_channels));
memset(led_counts, 0, sizeof(led_counts));
}
ASRockGPUSMBusController::~ASRockGPUSMBusController()
{
}
std::string ASRockGPUSMBusController::GetDeviceLocation()
{
std::string return_string(bus->info.device_name);
char addr[5];
snprintf(addr, 5, "0x%02X", dev);
return_string.append(", address ");
return_string.append(addr);
return("I2C: " + return_string);
}
std::string ASRockGPUSMBusController::GetDeviceName()
{
return(device_name);
}
bool ASRockGPUSMBusController::WriteBlock(uint8_t command, uint8_t subcommand, const uint8_t* data, uint8_t len)
{
/*-----------------------------------------------------*\
| SignalRGB packet format |
| [command, 0, subcommand, data...] |
| Uses pure I2C rather than SMBus |
\*-----------------------------------------------------*/
uint8_t packet[12];
memset(packet, 0, sizeof(packet));
packet[0] = command;
packet[1] = 0x00;
packet[2] = subcommand;
if(data != nullptr && len > 0)
{
uint8_t copy_len = (len > 9) ? 9 : len;
memcpy(&packet[3], data, copy_len);
}
int size = 12;
int result = bus->i2c_write_block(dev, size, packet);
if(result < 0)
{
LOG_DEBUG("[ASRock GPU] Write block failed: %d", result);
return(false);
}
return(true);
}
bool ASRockGPUSMBusController::ReadBlock(uint8_t* response, uint8_t len)
{
memset(response, 0, len);
int size = len;
int result = bus->i2c_read_block(dev, &size, response);
if(result < 0)
{
LOG_DEBUG("[ASRock GPU] Read block failed: %d", result);
return(false);
}
return(true);
}
bool ASRockGPUSMBusController::DetectChannels()
{
/*-----------------------------------------------------*\
| Get enabled channels |
| Command 0x14, subcommand 0x01 |
\*-----------------------------------------------------*/
if(!WriteBlock(0x14, 0x01, nullptr, 0))
{
LOG_WARNING("[ASRock GPU] Failed to request enabled channels");
return(false);
}
std::this_thread::sleep_for(20ms);
uint8_t response[I2C_SMBUS_BLOCK_MAX];
if(!ReadBlock(response, sizeof(response)))
{
LOG_WARNING("[ASRock GPU] Failed to read enabled channels");
return(false);
}
LOG_TRACE("[ASRock GPU] Enabled channels response: %02X %02X %02X %02X %02X %02X", response[0], response[1], response[2], response[3], response[4], response[5]);
/*-----------------------------------------------------*\
| Response format |
| Bytes 4-5 contain the channel bitmap |
\*-----------------------------------------------------*/
uint16_t channel_bits = response[4] | (response[5] << 8);
LOG_TRACE("[ASRock GPU] Channel bits: 0x%04X", channel_bits);
/*-----------------------------------------------------*\
| Get LED counts |
| Command 0x14, subcommand 0x02 |
\*-----------------------------------------------------*/
if(!WriteBlock(0x14, 0x02, nullptr, 0))
{
LOG_WARNING("[ASRock GPU] Failed to request LED counts");
return(false);
}
std::this_thread::sleep_for(20ms);
if(!ReadBlock(response, sizeof(response)))
{
LOG_WARNING("[ASRock GPU] Failed to read LED counts");
return(false);
}
num_channels = 0;
for(int i = 0; i < 16; i++)
{
if(channel_bits & (1 << i))
{
enabled_channels[num_channels] = i;
led_counts[num_channels] = response[4 + i];
const char* name = "Unknown";
if(i == ASROCK_GPU_CHANNEL_ARGB_HEADER)
{
name = "ARGB Header";
}
else if(i == ASROCK_GPU_CHANNEL_TOP_SIDE)
{
name = "Top Side";
}
else if(i == ASROCK_GPU_CHANNEL_FAN)
{
name = "Fan";
}
LOG_DEBUG("[ASRock GPU] Channel %d (%s) active with %d LEDs", i, name, response[4 + i]);
num_channels++;
}
}
LOG_DEBUG("[ASRock GPU] Found %d active channels", num_channels);
return(num_channels > 0);
}
void ASRockGPUSMBusController::SetChannelColor(uint8_t channel_idx, uint8_t red, uint8_t green, uint8_t blue)
{
/*-----------------------------------------------------*\
| SignalRGB color packet |
| Command 0x10, subcommand is the channel index |
\*-----------------------------------------------------*/
uint8_t data[8] =
{
0x01, // Mode: direct/static
red,
green,
blue,
0x80, // Brightness
0xFF, // Effect speed
0x00, // Effect direction
0x1A // Unknown constant
};
if(!WriteBlock(0x10, channel_idx, data, sizeof(data)))
{
LOG_WARNING("[ASRock GPU] Failed to set color for channel %d", channel_idx);
}
}
@@ -1,55 +0,0 @@
/*---------------------------------------------------------*\
| ASRockGPUSMBusController.h |
| |
| Driver for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include "i2c_smbus.h"
typedef uint8_t asrock_gpu_dev_id;
#define ASROCK_GPU_CONTROLLER_NAME "ASRock GPU SMBus Controller"
#define ASROCK_GPU_SMBUS_ADDRESS 0x36
/*-----------------------------------------------------*\
| ASRock GPU Channel Names (from SignalRGB) |
\*-----------------------------------------------------*/
enum
{
ASROCK_GPU_CHANNEL_ARGB_HEADER = 3,
ASROCK_GPU_CHANNEL_TOP_SIDE = 6,
ASROCK_GPU_CHANNEL_FAN = 7,
};
class ASRockGPUSMBusController
{
public:
ASRockGPUSMBusController(i2c_smbus_interface* bus, asrock_gpu_dev_id dev, std::string name);
~ASRockGPUSMBusController();
std::string GetDeviceLocation();
std::string GetDeviceName();
void SetChannelColor(uint8_t channel_idx, uint8_t red, uint8_t green, uint8_t blue);
bool DetectChannels();
uint8_t enabled_channels[16];
uint8_t led_counts[16];
uint8_t num_channels;
private:
i2c_smbus_interface* bus;
asrock_gpu_dev_id dev;
std::string device_name;
bool WriteBlock(uint8_t command, uint8_t subcommand, const uint8_t* data, uint8_t len);
bool ReadBlock(uint8_t* response, uint8_t len);
};
@@ -1,46 +0,0 @@
/*---------------------------------------------------------*\
| ASRockGPUSMBusControllerDetect.cpp |
| |
| Detector for ASRock GPU RGB controllers |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "DetectionManager.h"
#include "ASRockGPUSMBusController.h"
#include "LogManager.h"
#include "RGBController_ASRockGPUSMBus.h"
#include "i2c_smbus.h"
#include "pci_ids.h"
DetectedControllers DetectASRockGPUSMBusControllers(i2c_smbus_interface* bus, uint8_t address, const std::string& name)
{
DetectedControllers detected_controllers;
LOG_DEBUG("[ASRock GPU] Checking GPU I2C bus for RGB controller at 0x%02X", address);
if(bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE) < 0)
{
LOG_DEBUG("[ASRock GPU] No response at address 0x%02X", address);
return(detected_controllers);
}
LOG_DEBUG("[ASRock GPU] Device responded at 0x%02X, creating controller", address);
ASRockGPUSMBusController* controller = new ASRockGPUSMBusController(bus, address, name);
if(controller->DetectChannels())
{
detected_controllers.push_back(new RGBController_ASRockGPUSMBus(controller));
}
else
{
LOG_WARNING("[ASRock GPU] No active channels found, device not registered");
delete controller;
}
return(detected_controllers);
}
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);
@@ -1,127 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_ASRockGPUSMBus.cpp |
| |
| RGBController for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_ASRockGPUSMBus.h"
RGBController_ASRockGPUSMBus::RGBController_ASRockGPUSMBus(ASRockGPUSMBusController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetDeviceName();
vendor = "ASRock";
type = DEVICE_TYPE_GPU;
description = "ASRock GPU RGB Controller";
location = controller->GetDeviceLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = 0;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
}
RGBController_ASRockGPUSMBus::~RGBController_ASRockGPUSMBus()
{
delete controller;
}
void RGBController_ASRockGPUSMBus::SetupZones()
{
/*-----------------------------------------------------*\
| Set up zones - one zone per channel |
\*-----------------------------------------------------*/
for(unsigned int i = 0; i < controller->num_channels; i++)
{
uint8_t channel_idx = controller->enabled_channels[i];
zone new_zone;
new_zone.type = ZONE_TYPE_SINGLE;
new_zone.leds_min = 1;
new_zone.leds_max = 1;
new_zone.leds_count = 1;
/*-----------------------------------------------------*\
| Channel names from SignalRGB |
\*-----------------------------------------------------*/
if(channel_idx == ASROCK_GPU_CHANNEL_ARGB_HEADER)
{
new_zone.name = "ARGB Header";
}
else if(channel_idx == ASROCK_GPU_CHANNEL_TOP_SIDE)
{
new_zone.name = "Top Side";
}
else if(channel_idx == ASROCK_GPU_CHANNEL_FAN)
{
new_zone.name = "Fan";
}
else
{
new_zone.name = "Channel " + std::to_string(channel_idx);
}
zones.push_back(new_zone);
/*-----------------------------------------------------*\
| Set up LED for this zone |
\*-----------------------------------------------------*/
led new_led;
new_led.name = new_zone.name;
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_ASRockGPUSMBus::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*-----------------------------------------------------*\
| This device does not support resizing zones |
\*-----------------------------------------------------*/
}
void RGBController_ASRockGPUSMBus::DeviceUpdateLEDs()
{
for(unsigned int i = 0; i < controller->num_channels; i++)
{
uint8_t channel_idx = controller->enabled_channels[i];
RGBColor color = colors[i];
controller->SetChannelColor(channel_idx,
RGBGetRValue(color),
RGBGetGValue(color),
RGBGetBValue(color));
}
}
void RGBController_ASRockGPUSMBus::UpdateZoneLEDs(int zone)
{
uint8_t channel_idx = controller->enabled_channels[zone];
RGBColor color = colors[zone];
controller->SetChannelColor(channel_idx,
RGBGetRValue(color),
RGBGetGValue(color),
RGBGetBValue(color));
}
void RGBController_ASRockGPUSMBus::UpdateSingleLED(int led)
{
UpdateZoneLEDs(led);
}
void RGBController_ASRockGPUSMBus::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
@@ -1,34 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_ASRockGPUSMBus.h |
| |
| RGBController for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "ASRockGPUSMBusController.h"
class RGBController_ASRockGPUSMBus : public RGBController
{
public:
RGBController_ASRockGPUSMBus(ASRockGPUSMBusController* controller_ptr);
~RGBController_ASRockGPUSMBus();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
ASRockGPUSMBusController* controller;
};
@@ -1,50 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidKeyboard.cpp |
| |
| Detector for Acer Nitro HID Keyboard |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <vector>
#include "DetectionManager.h"
#include "AcerNitroHidKeyboardController.h"
#include "RGBController_AcerNitroHidKeyboard.h"
#include "RGBController_AcerNitroHidLed.h"
/*---------------------------------------------------------*\
| HID vendor and product IDs for ENEK EC in Acer Nitro |
\*---------------------------------------------------------*/
#define ACER_ENEC_VID 0x0CF2
#define ACER_ENEC_PID 0x5130
#define ACER_ENEC_USAGE_PAGE 0xFF5A
#define ACER_ENEC_USAGE 0x01
DetectedControllers DetectAcerHIDControllers(hid_device_info* info, const std::string&)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
/*-------------------------------------------------*\
| Controller is shared between keyboard and onboard |
| led |
\*-------------------------------------------------*/
std::shared_ptr<AcerNitroHidKeyboardController> controller = std::make_shared<AcerNitroHidKeyboardController>(dev, info->path);
RGBController_AcerNitroHidKeyboard* rgb_kb_controller = new RGBController_AcerNitroHidKeyboard(controller);
RGBController_AcerNitroHidLed* rgb_led_controller = new RGBController_AcerNitroHidLed(controller);
detected_controllers.push_back(rgb_kb_controller);
detected_controllers.push_back(rgb_led_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_PU("AcerNitroHidKeyboardController", DetectAcerHIDControllers, ACER_ENEC_VID, ACER_ENEC_PID, ACER_ENEC_USAGE_PAGE, ACER_ENEC_USAGE);
@@ -1,80 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidKeyboard.cpp |
| |
| Driver for Acer Nitro Keyboard |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "AcerNitroHidKeyboardController.h"
AcerNitroHidKeyboardController::AcerNitroHidKeyboardController(hid_device *dev_handle, const char *path)
{
this->location = path;
this->dev = dev_handle;
device_name = "Acer Nitro HID Keyboard";
}
AcerNitroHidKeyboardController::~AcerNitroHidKeyboardController()
{
if(dev)
{
hid_close(dev);
}
}
std::string AcerNitroHidKeyboardController::GetLocation() const
{
return("HID: " + location);
}
std::string AcerNitroHidKeyboardController::GetDeviceName() const
{
return(device_name);
}
void AcerNitroHidKeyboardController::SetLEDs(const std::vector<RGBColor>& colors, unsigned char brightness, unsigned char device_id)
{
std::lock_guard<std::mutex> guard(device_mutex);
for(unsigned int led_idx = 0; (led_idx < colors.size()) && (led_idx < ACER_HID_NUM_ZONES); led_idx++)
{
/*-------------------------------------------------*\
| Convert colors to RGB format |
\*-------------------------------------------------*/
unsigned char r = RGBGetRValue(colors[led_idx]);
unsigned char g = RGBGetGValue(colors[led_idx]);
unsigned char b = RGBGetBValue(colors[led_idx]);
/*-------------------------------------------------*\
| Send packet |
\*-------------------------------------------------*/
unsigned char zone_select = (unsigned char)(1<<led_idx);
unsigned char packet[ACER_HID_PACKET_SIZE] = {0xA4, device_id, ACER_HID_MODE_STATIC, brightness, 0, 1, r, g, b, zone_select, 0};
hid_send_feature_report(dev, packet, ACER_HID_PACKET_SIZE);
}
}
void AcerNitroHidKeyboardController::SetAllLEDs(const RGBColor &color, unsigned char brightness, unsigned char speed, unsigned char direction, unsigned char mode, unsigned char device_id)
{
std::lock_guard<std::mutex> guard(device_mutex);
/*-----------------------------------------------------*\
| Convert color to RGB format |
\*-----------------------------------------------------*/
unsigned char r = RGBGetRValue(color);
unsigned char g = RGBGetGValue(color);
unsigned char b = RGBGetBValue(color);
/*-----------------------------------------------------*\
| Send packet |
| Zone select = All zones |
\*-----------------------------------------------------*/
unsigned char zone_select = 0x0F;
unsigned char packet[ACER_HID_PACKET_SIZE] = {0xA4, device_id, mode, brightness, speed, direction, r, g, b, zone_select, 0};
hid_send_feature_report(dev, packet, ACER_HID_PACKET_SIZE);
}
@@ -1,56 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidKeyboard.cpp |
| |
| Driver for Acer Nitro Keyboard |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
enum
{
ACER_HID_MODE_DIRECT = 1,
ACER_HID_MODE_STATIC = 2,
ACER_HID_MODE_BREATHING = 4,
ACER_HID_MODE_NEON = 5,
ACER_HID_MODE_OPMODE = 6,
ACER_HID_MODE_WAVE = 7,
ACER_HID_MODE_SHIFTING = 8,
ACER_HID_MODE_ZOOM = 9,
ACER_HID_MODE_METEOR = 10,
ACER_HID_MODE_TWINKLING = 11,
};
#define ACER_HID_NAME_BUFFER_SIZE 255
#define ACER_HID_NUM_ZONES 4
#define ACER_HID_PACKET_SIZE 11
#include <mutex>
#include <string>
#include <vector>
#include <hidapi.h>
#include "RGBControllerInterface.h"
class AcerNitroHidKeyboardController
{
public:
AcerNitroHidKeyboardController(hid_device *dev_handle, const char *path);
~AcerNitroHidKeyboardController();
std::string GetDeviceName() const;
std::string GetSerial() const;
std::string GetLocation() const;
void SetLEDs(const std::vector<RGBColor> &colors, unsigned char brightness, unsigned char device_id);
void SetAllLEDs(const RGBColor &color, unsigned char brightness, unsigned char speed, unsigned char direction, unsigned char mode, unsigned char device_id);
private:
hid_device *dev;
std::string device_name;
std::string location;
std::mutex device_mutex;
};
@@ -1,231 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidKeyboard.cpp |
| |
| RGBController for Acer Nitro HID Keyboard |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_AcerNitroHidKeyboard.h"
// /**------------------------------------------------------------------*\
// @name Acer Nitro HID Keyboard
// @category Laptop
// @type HID
// @save :x:
// @direct :white_check_mark:
// @effects :white_check_mark:
// @detectors DetectAcerHIDControllers
// @comment
// \*-------------------------------------------------------------------*/
RGBController_AcerNitroHidKeyboard::RGBController_AcerNitroHidKeyboard(std::shared_ptr<AcerNitroHidKeyboardController> controller)
{
this->controller = controller;
device_id = 0x21;
name = "Acer Nitro HID Keyboard";
vendor = "Acer";
type = DEVICE_TYPE_LAPTOP;
description = "Acer Nitro HID Keyboard Device";
location = controller->GetLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = ACER_HID_MODE_DIRECT;
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 = 100;
Direct.brightness = 100;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = ACER_HID_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.brightness_min = 0;
Static.brightness_max = 100;
Static.brightness = 100;
Static.colors_min = 1;
Static.colors_max = 1;
Static.colors.resize(1);
Static.speed = 0;
modes.push_back(Static);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = ACER_HID_MODE_BREATHING;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
Breathing.brightness_min = 0;
Breathing.brightness_max = 100;
Breathing.brightness = 100;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.speed_min = 1;
Breathing.speed_max = 9;
Breathing.speed = 4;
Breathing.colors.resize(1);
modes.push_back(Breathing);
mode Neon;
Neon.name = "Neon";
Neon.value = ACER_HID_MODE_NEON;
Neon.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Neon.color_mode = MODE_COLORS_NONE;
Neon.brightness_min = 0;
Neon.brightness_max = 100;
Neon.brightness = 100;
Neon.speed_min = 1;
Neon.speed_max = 9;
Neon.speed = 4;
modes.push_back(Neon);
mode Wave;
Wave.name = "Wave";
Wave.value = ACER_HID_MODE_WAVE;
Wave.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR;
Wave.color_mode = MODE_COLORS_NONE;
Wave.brightness_min = 0;
Wave.brightness_max = 100;
Wave.brightness = 100;
Wave.speed_min = 1;
Wave.speed_max = 9;
Wave.speed = 4;
Wave.direction = 0;
modes.push_back(Wave);
mode Shifting;
Shifting.name = "Shifting";
Shifting.value = ACER_HID_MODE_SHIFTING;
Shifting.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Shifting.color_mode = MODE_COLORS_NONE;
Shifting.brightness_min = 0;
Shifting.brightness_max = 100;
Shifting.brightness = 100;
Shifting.speed_min = 1;
Shifting.speed_max = 9;
Shifting.speed = 4;
modes.push_back(Shifting);
mode Zoom;
Zoom.name = "Zoom";
Zoom.value = ACER_HID_MODE_ZOOM;
Zoom.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Zoom.color_mode = MODE_COLORS_NONE;
Zoom.brightness_min = 0;
Zoom.brightness_max = 100;
Zoom.brightness = 100;
Zoom.speed_min = 1;
Zoom.speed_max = 9;
Zoom.speed = 4;
modes.push_back(Zoom);
mode Meteor;
Meteor.name = "Meteor";
Meteor.value = ACER_HID_MODE_METEOR;
Meteor.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Meteor.color_mode = MODE_COLORS_NONE;
Meteor.brightness_min = 0;
Meteor.brightness_max = 100;
Meteor.brightness = 100;
Meteor.speed_min = 1;
Meteor.speed_max = 9;
Meteor.speed = 4;
modes.push_back(Meteor);
mode Twinkling;
Twinkling.name = "Twinkling";
Twinkling.value = ACER_HID_MODE_TWINKLING;
Twinkling.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Twinkling.color_mode = MODE_COLORS_NONE;
Twinkling.brightness_min = 0;
Twinkling.brightness_max = 100;
Twinkling.brightness = 100;
Twinkling.colors_min = 1;
Twinkling.colors_max = 1;
Twinkling.speed_min = 1;
Twinkling.speed_max = 9;
Twinkling.speed = 4;
modes.push_back(Twinkling);
SetupZones();
}
RGBController_AcerNitroHidKeyboard::~RGBController_AcerNitroHidKeyboard()
{
Shutdown();
}
void RGBController_AcerNitroHidKeyboard::SetupZones()
{
zone* new_zone = new zone;
new_zone->name = "Keyboard Backlight Zone";
new_zone->type = ZONE_TYPE_LINEAR;
new_zone->leds_min = 4;
new_zone->leds_max = 4;
new_zone->leds_count = 4;
zones.push_back(*new_zone);
for(std::size_t zone_idx = 0; zone_idx < ACER_HID_NUM_ZONES; zone_idx++)
{
led* new_led = new led();
new_led->name = "Keyboard Backlight Zone ";
new_led->name.append(std::to_string(zone_idx + 1));
new_led->value = (unsigned int)leds.size();
leds.push_back(*new_led);
}
SetupColors();
}
void RGBController_AcerNitroHidKeyboard::DeviceUpdateLEDs()
{
mode& current_mode = modes[active_mode];
if(current_mode.value == ACER_HID_MODE_DIRECT)
{
controller->SetLEDs(colors, current_mode.brightness, device_id);
}
else
{
RGBColor color = current_mode.colors.size()==0? (RGBColor)0 : current_mode.colors[0];
unsigned char direction = 1;
if((modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) && modes[active_mode].direction)
{
direction = 2;
}
controller->SetAllLEDs(color, current_mode.brightness, current_mode.speed, direction, current_mode.value, device_id);
}
}
void RGBController_AcerNitroHidKeyboard::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_AcerNitroHidKeyboard::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_AcerNitroHidKeyboard::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
@@ -1,33 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidKeyboard.cpp |
| |
| RGBController for Acer Nitro HID Keyboard |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "AcerNitroHidKeyboardController.h"
class RGBController_AcerNitroHidKeyboard : public RGBController
{
public:
RGBController_AcerNitroHidKeyboard(std::shared_ptr<AcerNitroHidKeyboardController> controller);
~RGBController_AcerNitroHidKeyboard();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
private:
std::shared_ptr<AcerNitroHidKeyboardController> controller;
unsigned int device_id;
};
@@ -1,152 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidLed.cpp |
| |
| RGBController for Acer Nitro HID LED |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_AcerNitroHidLed.h"
// /**------------------------------------------------------------------*\
// @name Acer Nitro HID Led
// @category Laptop
// @type HID
// @save :x:
// @direct :white_check_mark:
// @effects :white_check_mark:
// @detectors DetectAcerHIDControllers
// @comment
// \*-------------------------------------------------------------------*/
RGBController_AcerNitroHidLed::RGBController_AcerNitroHidLed(std::shared_ptr<AcerNitroHidKeyboardController> controller)
{
this->controller = controller;
device_id = 0x65;
name = "Acer Nitro HID LED Device";
vendor = "Acer";
type = DEVICE_TYPE_LIGHT;
description = "Acer Nitro HID LED Device";
location = controller->GetLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = ACER_HID_MODE_DIRECT;
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 = 100;
Direct.brightness = 100;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = ACER_HID_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.brightness_min = 0;
Static.brightness_max = 100;
Static.brightness = 100;
Static.colors_min = 1;
Static.colors_max = 1;
Static.colors.resize(1);
Static.speed = 0;
modes.push_back(Static);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = ACER_HID_MODE_BREATHING;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
Breathing.brightness_min = 0;
Breathing.brightness_max = 100;
Breathing.brightness = 100;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.speed_min = 1;
Breathing.speed_max = 5;
Breathing.speed = 4;
Breathing.colors.resize(1);
modes.push_back(Breathing);
mode Neon;
Neon.name = "Neon";
Neon.value = ACER_HID_MODE_NEON;
Neon.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Neon.color_mode = MODE_COLORS_NONE;
Neon.brightness_min = 0;
Neon.brightness_max = 100;
Neon.brightness = 100;
Neon.speed_min = 1;
Neon.speed_max = 5;
Neon.speed = 4;
modes.push_back(Neon);
SetupZones();
}
RGBController_AcerNitroHidLed::~RGBController_AcerNitroHidLed()
{
Shutdown();
}
void RGBController_AcerNitroHidLed::SetupZones()
{
zone* new_zone = new zone;
new_zone->name = "LED";
new_zone->type = ZONE_TYPE_LINEAR;
new_zone->leds_min = 1;
new_zone->leds_max = 1;
new_zone->leds_count = 1;
zones.push_back(*new_zone);
led* new_led = new led();
new_led->name = "LED";
new_led->value = (unsigned int)leds.size();
leds.push_back(*new_led);
SetupColors();
}
void RGBController_AcerNitroHidLed::DeviceUpdateLEDs()
{
mode& current_mode = modes[active_mode];
if(current_mode.value == ACER_HID_MODE_DIRECT)
{
controller->SetLEDs(colors, current_mode.brightness, device_id);
}
else
{
RGBColor color = current_mode.colors.size()==0? (RGBColor)0 : current_mode.colors[0];
unsigned char direction = 1;
if((modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) && modes[active_mode].direction)
{
direction = 2;
}
controller->SetAllLEDs(color, current_mode.brightness, current_mode.speed, direction, current_mode.value, device_id);
}
}
void RGBController_AcerNitroHidLed::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_AcerNitroHidLed::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_AcerNitroHidLed::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
@@ -1,35 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidLed.cpp |
| |
| RGBController for Acer Nitro HID LED |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "AcerNitroHidKeyboardController.h"
#include "RGBController_AcerNitroHidKeyboard.h"
class RGBController_AcerNitroHidLed : public RGBController
{
public:
RGBController_AcerNitroHidLed(std::shared_ptr<AcerNitroHidKeyboardController> controller);
~RGBController_AcerNitroHidLed();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
private:
std::shared_ptr<AcerNitroHidKeyboardController> controller;
unsigned int device_id;
};
@@ -1,25 +1,22 @@
/*---------------------------------------------------------*\
| ClevoKeyboardController.cpp |
| |
| Driver for Clevo per-key RGB keyboard |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| Driver for Clevo laptop per-key RGB keyboard (ITE 8291) |
| Protocol based on tuxedo-drivers ite_8291 module |
| |
| Kyle Cascade ([email protected]) 16 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <algorithm>
#include <cstring>
#include "ClevoKeyboardController.h"
#include "StringUtils.h"
ClevoKeyboardController::ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info, clevo_keyboard_type kb_type)
ClevoKeyboardController::ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info)
{
dev = dev_handle;
type = kb_type;
location = info.path;
version = info.release_number;
}
@@ -29,31 +26,6 @@ ClevoKeyboardController::~ClevoKeyboardController()
hid_close(dev);
}
clevo_keyboard_type ClevoKeyboardController::GetType()
{
return type;
}
int ClevoKeyboardController::GetNumRows()
{
return(type == CLEVO_KB_ITE829X) ? CLEVO_829X_NUM_ROWS : CLEVO_8291_NUM_ROWS;
}
int ClevoKeyboardController::GetNumCols()
{
return(type == CLEVO_KB_ITE829X) ? CLEVO_829X_NUM_COLS : CLEVO_8291_NUM_COLS;
}
int ClevoKeyboardController::GetNumLEDs()
{
return GetNumRows() * GetNumCols();
}
unsigned char ClevoKeyboardController::GetBrightnessMax()
{
return(type == CLEVO_KB_ITE829X) ? CLEVO_829X_BRIGHTNESS_MAX : CLEVO_8291_BRIGHTNESS_MAX;
}
std::string ClevoKeyboardController::GetDeviceLocation()
{
return("HID: " + location);
@@ -79,355 +51,147 @@ std::string ClevoKeyboardController::GetFirmwareVersion()
return(version_string);
}
/*---------------------------------------------------------*\
| ITE 8291: 8-byte feature report, no report ID |
\*---------------------------------------------------------*/
void ClevoKeyboardController::WriteControl8291(unsigned char* data)
void ClevoKeyboardController::WriteControl(unsigned char* data)
{
hid_send_feature_report(dev, data, CLEVO_8291_REPORT_SIZE);
hid_send_feature_report(dev, data, CLEVO_KEYBOARD_REPORT_SIZE);
}
void ClevoKeyboardController::WriteRowData(unsigned char* data)
{
hid_write(dev, data, CLEVO_8291_ROW_DATA_SIZE);
}
/*---------------------------------------------------------*\
| ITE 829x: 6-byte feature report with report ID 0xCC |
\*---------------------------------------------------------*/
void ClevoKeyboardController::SendCommand829x(unsigned char cmd, unsigned char d0, unsigned char d1, unsigned char d2, unsigned char d3)
{
unsigned char buf[CLEVO_829X_REPORT_SIZE];
buf[0] = CLEVO_829X_REPORT_ID;
buf[1] = cmd;
buf[2] = d0;
buf[3] = d1;
buf[4] = d2;
buf[5] = d3;
hid_send_feature_report(dev, buf, CLEVO_829X_REPORT_SIZE);
hid_write(dev, data, CLEVO_KEYBOARD_ROW_DATA_SIZE);
}
void ClevoKeyboardController::TurnOff()
{
if(type == CLEVO_KB_ITE829X)
{
SendCommand829x(CLEVO_829X_CMD_BRIGHTNESS);
return;
}
/*---------------------------------------------------------*\
| Turn off: 08 01 00 00 00 00 00 00 |
\*---------------------------------------------------------*/
unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE];
unsigned char buf[CLEVO_8291_REPORT_SIZE];
memset(buf, 0x00, CLEVO_8291_REPORT_SIZE);
buf[0] = CLEVO_8291_CMD_MODE;
buf[1] = CLEVO_8291_POWER_OFF;
WriteControl8291(buf);
memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
buf[0] = 0x08;
buf[1] = 0x01;
WriteControl(buf);
}
void ClevoKeyboardController::SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char behaviour)
{
if(type == CLEVO_KB_ITE829X)
{
SendCommand829x(CLEVO_829X_CMD_BRIGHTNESS, brightness, speed);
return;
}
/*---------------------------------------------------------*\
| Set params: 08 [power] [mode] [speed] [brightness] 08 |
| [behaviour] 00 |
| power: 01=off, 02=on |
\*---------------------------------------------------------*/
unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE];
unsigned char buf[CLEVO_8291_REPORT_SIZE];
memset(buf, 0x00, CLEVO_8291_REPORT_SIZE);
buf[0] = CLEVO_8291_CMD_MODE;
buf[1] = CLEVO_8291_POWER_ON;
memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
buf[0] = 0x08;
buf[1] = 0x02; // Power on
buf[2] = mode;
buf[3] = speed;
buf[4] = brightness;
buf[5] = CLEVO_8291_MODE_FLAG;
buf[5] = 0x08;
buf[6] = behaviour;
WriteControl8291(buf);
WriteControl(buf);
}
void ClevoKeyboardController::SetModeColor(unsigned char color_idx, RGBColor color)
void ClevoKeyboardController::SetModeColor(unsigned char color_idx, unsigned char red, unsigned char green, unsigned char blue)
{
if(type == CLEVO_KB_ITE829X)
{
return;
}
/*---------------------------------------------------------*\
| Set color define: 14 00 [index] R G B 00 00 |
| index: 1-7 for built-in effects |
\*---------------------------------------------------------*/
unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE];
unsigned char buf[CLEVO_8291_REPORT_SIZE];
memset(buf, 0x00, CLEVO_8291_REPORT_SIZE);
buf[0] = CLEVO_8291_CMD_COLOR;
memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
buf[0] = 0x14;
buf[1] = 0x00;
buf[2] = color_idx;
buf[3] = RGBGetRValue(color);
buf[4] = RGBGetGValue(color);
buf[5] = RGBGetBValue(color);
WriteControl8291(buf);
buf[3] = red;
buf[4] = green;
buf[5] = blue;
WriteControl(buf);
}
void ClevoKeyboardController::SetBrightnessSpeed829x(unsigned char brightness, unsigned char speed)
void ClevoKeyboardController::SendColors(unsigned char* color_data, unsigned char brightness)
{
if(brightness > CLEVO_829X_BRIGHTNESS_MAX)
{
brightness = CLEVO_829X_BRIGHTNESS_MAX;
}
if(brightness != current_brightness_829x || speed != current_speed_829x)
{
current_brightness_829x = brightness;
current_speed_829x = speed;
SendCommand829x(CLEVO_829X_CMD_BRIGHTNESS, brightness, speed);
}
}
void ClevoKeyboardController::SendColorSlot829x(unsigned char cmd, unsigned char slot, RGBColor color)
{
SendCommand829x(cmd, slot, RGBGetRValue(color), RGBGetGValue(color), RGBGetBValue(color));
}
/*---------------------------------------------------------*\
| ITE 829x: Save/Clear BIOS keyboard state |
\*---------------------------------------------------------*/
void ClevoKeyboardController::SaveToBios829x()
{
SendCommand829x(CLEVO_829X_CMD_SAVE_BIOS, 0x01);
}
void ClevoKeyboardController::ClearBiosSave829x()
{
SendCommand829x(CLEVO_829X_CMD_SAVE_BIOS, 0x00);
}
void ClevoKeyboardController::StopAnimation829x()
{
SendCommand829x(CLEVO_829X_CMD_ANIM, CLEVO_829X_CMD_STOP_ANIM);
}
void ClevoKeyboardController::SetEffect829x(unsigned char mode_value, const std::vector<RGBColor>& colors, unsigned int direction, unsigned int color_mode)
{
bool mode_changed = (mode_value != current_mode_829x);
current_mode_829x = mode_value;
bool use_custom_color = (color_mode != MODE_COLORS_RANDOM) && !colors.empty();
/*---------------------------------------------------------*\
| Per-key RGB mode (mode 0x33) |
| 1. Set params with mode 0x33 and brightness |
| 2. For each row 0-5: |
| - Announce row: 16 00 [row] 00 00 00 00 00 |
| - Send 65 bytes row data via output report |
| |
| Row data format (65 bytes): |
| [0x00 padding][0x00 padding] |
| [B0..B20][G0..G20][R0..R20] |
\*---------------------------------------------------------*/
unsigned char ctrl_buf[CLEVO_KEYBOARD_REPORT_SIZE];
unsigned char row_buf[CLEVO_KEYBOARD_ROW_DATA_SIZE];
/*---------------------------------------------------------*\
| Animation modes: send [CC, 00, mode_id] on mode change |
| Clamp brightness |
\*---------------------------------------------------------*/
struct anim_entry { unsigned char mode; unsigned char anim_id; };
static const anim_entry anim_modes[] =
if(brightness > CLEVO_KEYBOARD_BRIGHTNESS_MAX)
{
{ CLEVO_829X_MODE_WAVE, 0x04 },
{ CLEVO_829X_MODE_RANDOM, 0x09 },
{ CLEVO_829X_MODE_SCAN, 0x0A },
{ CLEVO_829X_MODE_SNAKE, 0x0B },
{ CLEVO_829X_MODE_RAINBOW_CYCLE, 0x02 },
{ CLEVO_829X_MODE_RAINBOW_GRAD, 0x04 },
};
const anim_entry* anim_it = std::find_if(std::begin(anim_modes), std::end(anim_modes),
[mode_value](const anim_entry& e) { return e.mode == mode_value; });
if(anim_it != std::end(anim_modes) && mode_changed)
{
SendCommand829x(CLEVO_829X_CMD_ANIM, anim_it->anim_id);
brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
}
/*---------------------------------------------------------*\
| Direction-based effects (Wave/Snake) |
| Slot = base + direction_index |
| Custom color: base = 0xA1, Preset: base = 0x71 |
| Set params for per-key mode |
\*---------------------------------------------------------*/
/*---------------------------------------------------------*\
| Map OpenRGB direction enum to firmware slot index |
\*---------------------------------------------------------*/
static const unsigned char dir_to_wave_idx[] =
{
6, /* MODE_DIRECTION_LEFT */
7, /* MODE_DIRECTION_RIGHT */
4, /* MODE_DIRECTION_UP */
5, /* MODE_DIRECTION_DOWN */
6, /* MODE_DIRECTION_HORIZONTAL */
4, /* MODE_DIRECTION_VERTICAL */
0, /* MODE_DIRECTION_UP_LEFT */
1, /* MODE_DIRECTION_UP_RIGHT */
2, /* MODE_DIRECTION_DOWN_LEFT */
3, /* MODE_DIRECTION_DOWN_RIGHT */
};
static const unsigned char dir_to_snake_idx[] =
{
0, /* MODE_DIRECTION_LEFT */
0, /* MODE_DIRECTION_RIGHT */
0, /* MODE_DIRECTION_UP */
0, /* MODE_DIRECTION_DOWN */
0, /* MODE_DIRECTION_HORIZONTAL */
0, /* MODE_DIRECTION_VERTICAL */
0, /* MODE_DIRECTION_UP_LEFT */
1, /* MODE_DIRECTION_UP_RIGHT */
2, /* MODE_DIRECTION_DOWN_LEFT */
3, /* MODE_DIRECTION_DOWN_RIGHT */
};
if((mode_value == CLEVO_829X_MODE_WAVE || mode_value == CLEVO_829X_MODE_RAINBOW_GRAD) && direction < sizeof(dir_to_wave_idx))
{
unsigned char base = use_custom_color ? CLEVO_829X_COLOR_SLOT_BASE : CLEVO_829X_PRESET_SLOT_BASE;
unsigned char idx = dir_to_wave_idx[direction];
unsigned char slot = base + idx;
if(use_custom_color)
{
SendColorSlot829x(CLEVO_829X_CMD_WAVE_COLOR, slot, colors[0]);
}
if(!use_custom_color)
{
SendCommand829x(CLEVO_829X_CMD_WAVE_COLOR, slot);
}
}
if(mode_value == CLEVO_829X_MODE_SNAKE && direction < sizeof(dir_to_snake_idx))
{
unsigned char base = use_custom_color ? CLEVO_829X_COLOR_SLOT_BASE : CLEVO_829X_PRESET_SLOT_BASE;
unsigned char idx = dir_to_snake_idx[direction];
unsigned char slot = base + idx;
if(use_custom_color)
{
SendColorSlot829x(CLEVO_829X_CMD_SNAKE_COLOR, slot, colors[0]);
}
if(!use_custom_color)
{
SendCommand829x(CLEVO_829X_CMD_SNAKE_COLOR, slot);
}
}
/*---------------------------------------------------------*\
| Breathing/Flashing: random or custom color |
\*---------------------------------------------------------*/
if(mode_value == CLEVO_829X_MODE_BREATH && use_custom_color)
{
SendColorSlot829x(0x0A, CLEVO_829X_COLOR_CUSTOM, colors[0]);
}
if(mode_value == CLEVO_829X_MODE_BREATH && !use_custom_color)
{
SendCommand829x(0x0A);
}
if(mode_value == CLEVO_829X_MODE_BLINK && use_custom_color)
{
SendColorSlot829x(0x0B, CLEVO_829X_COLOR_CUSTOM, colors[0]);
}
if(mode_value == CLEVO_829X_MODE_BLINK && !use_custom_color)
{
SendCommand829x(0x0B);
}
/*---------------------------------------------------------*\
| Random: custom color only (multicolor is the default) |
\*---------------------------------------------------------*/
if(mode_value == CLEVO_829X_MODE_RANDOM && use_custom_color)
{
SendColorSlot829x(CLEVO_829X_CMD_RANDOM_COLOR, CLEVO_829X_COLOR_SLOT_BASE, colors[0]);
}
/*---------------------------------------------------------*\
| Scan: 2 custom color bands |
\*---------------------------------------------------------*/
if(mode_value == CLEVO_829X_MODE_SCAN)
{
for(size_t i = 0; i < colors.size() && i < 2; i++)
{
SendColorSlot829x(CLEVO_829X_CMD_SCAN_COLOR, CLEVO_829X_COLOR_SLOT_BASE + i, colors[i]);
}
}
/*---------------------------------------------------------*\
| Fn Highlight: per-key color on function keys |
\*---------------------------------------------------------*/
if(mode_value == CLEVO_829X_MODE_FN_COLOR && !colors.empty())
{
static const unsigned char fn_keys[] =
{
0x01, 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0A, 0x0B, 0x0C, 0x21, 0x23, 0x31, 0x32, 0x33,
0x53, 0x73, 0xA2
};
SendCommand829x(CLEVO_829X_CMD_ANIM, CLEVO_829X_CMD_CLEAR);
for(unsigned char key : fn_keys)
{
SendCommand829x(CLEVO_829X_CMD_SET_LED, key,
RGBGetRValue(colors[0]),
RGBGetGValue(colors[0]),
RGBGetBValue(colors[0]));
}
}
}
void ClevoKeyboardController::SendColors(const unsigned char* color_data, unsigned char brightness, unsigned char speed)
{
int num_rows = GetNumRows();
int num_cols = GetNumCols();
if(type == CLEVO_KB_ITE829X)
{
if(brightness > CLEVO_829X_BRIGHTNESS_MAX)
{
brightness = CLEVO_829X_BRIGHTNESS_MAX;
}
SendCommand829x(CLEVO_829X_CMD_BRIGHTNESS, brightness, speed);
for(int row = 0; row < num_rows; row++)
{
for(int col = 0; col < num_cols; col++)
{
int idx = (row * num_cols + col) * 3;
SendCommand829x(CLEVO_829X_CMD_SET_LED,
CLEVO_829X_LED_ID(row, col),
color_data[idx + 0],
color_data[idx + 1],
color_data[idx + 2]);
}
}
return;
}
/*---------------------------------------------------------*\
| ITE 8291: row-based color transfer |
\*---------------------------------------------------------*/
unsigned char ctrl_buf[CLEVO_8291_REPORT_SIZE];
unsigned char row_buf[CLEVO_8291_ROW_DATA_SIZE];
if(brightness > CLEVO_8291_BRIGHTNESS_MAX)
{
brightness = CLEVO_8291_BRIGHTNESS_MAX;
}
memset(ctrl_buf, 0x00, CLEVO_8291_REPORT_SIZE);
ctrl_buf[0] = CLEVO_8291_CMD_MODE;
ctrl_buf[1] = CLEVO_8291_POWER_ON;
ctrl_buf[2] = CLEVO_KEYBOARD_MODE_DIRECT;
memset(ctrl_buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
ctrl_buf[0] = 0x08;
ctrl_buf[1] = 0x02; // Power on
ctrl_buf[2] = CLEVO_KEYBOARD_MODE_DIRECT; // Per-key mode
ctrl_buf[3] = 0x00; // Speed (unused)
ctrl_buf[4] = brightness;
WriteControl8291(ctrl_buf);
ctrl_buf[5] = 0x00;
ctrl_buf[6] = 0x00;
for(int row = 0; row < num_rows; row++)
WriteControl(ctrl_buf);
/*---------------------------------------------------------*\
| Send each row |
\*---------------------------------------------------------*/
for(int row = 0; row < CLEVO_KEYBOARD_NUM_ROWS; row++)
{
memset(ctrl_buf, 0x00, CLEVO_8291_REPORT_SIZE);
ctrl_buf[0] = CLEVO_8291_CMD_ROW;
/*-----------------------------------------------------*\
| Announce row data |
\*-----------------------------------------------------*/
memset(ctrl_buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
ctrl_buf[0] = 0x16;
ctrl_buf[1] = 0x00;
ctrl_buf[2] = row;
WriteControl8291(ctrl_buf);
memset(row_buf, 0x00, CLEVO_8291_ROW_DATA_SIZE);
WriteControl(ctrl_buf);
for(int col = 0; col < num_cols; col++)
/*-----------------------------------------------------*\
| Build row data buffer |
| Format: [pad][pad][B0..B20][G0..G20][R0..R20] |
\*-----------------------------------------------------*/
memset(row_buf, 0x00, CLEVO_KEYBOARD_ROW_DATA_SIZE);
for(int col = 0; col < CLEVO_KEYBOARD_NUM_COLS; col++)
{
int idx = (row * num_cols + col) * 3;
int led_idx = (row * CLEVO_KEYBOARD_NUM_COLS) + col;
int color_offset = led_idx * 3;
row_buf[2 + col] = color_data[idx + 2];
row_buf[2 + num_cols + col] = color_data[idx + 1];
row_buf[2 + num_cols * 2 + col] = color_data[idx + 0];
unsigned char red = color_data[color_offset + 0];
unsigned char green = color_data[color_offset + 1];
unsigned char blue = color_data[color_offset + 2];
/*-------------------------------------------------*\
| Row data layout (after 2-byte padding): |
| Bytes 2-22: Blue values for columns 0-20 |
| Bytes 23-43: Green values for columns 0-20 |
| Bytes 44-64: Red values for columns 0-20 |
\*-------------------------------------------------*/
row_buf[2 + col] = blue;
row_buf[2 + CLEVO_KEYBOARD_NUM_COLS + col] = green;
row_buf[2 + CLEVO_KEYBOARD_NUM_COLS*2 + col] = red;
}
WriteRowData(row_buf);
@@ -1,11 +1,9 @@
/*---------------------------------------------------------*\
| ClevoKeyboardController.h |
| |
| Driver for Clevo per-key RGB keyboard |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| Driver for Clevo laptop per-key RGB keyboard (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 16 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -14,75 +12,27 @@
#pragma once
#include <string>
#include <vector>
#include <hidapi.h>
#include "RGBController.h"
/*---------------------------------------------------------*\
| Device type enumeration |
\*---------------------------------------------------------*/
enum clevo_keyboard_type
{
CLEVO_KB_ITE8291,
CLEVO_KB_ITE829X,
};
/*-----------------------------------------------------*\
| ITE 8291 keyboard defines |
\*-----------------------------------------------------*/
#define CLEVO_KEYBOARD_REPORT_SIZE 8
#define CLEVO_KEYBOARD_ROW_DATA_SIZE 65
/*---------------------------------------------------------*\
| ITE 8291 keyboard defines (PID 0x600B) |
| 8 bytes, no report ID, row-based color data |
\*---------------------------------------------------------*/
#define CLEVO_8291_REPORT_SIZE 8
#define CLEVO_8291_ROW_DATA_SIZE 65
#define CLEVO_8291_NUM_ROWS 6
#define CLEVO_8291_NUM_COLS 21
#define CLEVO_8291_NUM_LEDS (CLEVO_8291_NUM_ROWS * CLEVO_8291_NUM_COLS)
#define CLEVO_8291_BRIGHTNESS_MAX 0x32
#define CLEVO_8291_CMD_MODE 0x08
#define CLEVO_8291_CMD_COLOR 0x14
#define CLEVO_8291_CMD_ROW 0x16
#define CLEVO_8291_POWER_OFF 0x01
#define CLEVO_8291_POWER_ON 0x02
#define CLEVO_8291_MODE_FLAG 0x08
#define CLEVO_KEYBOARD_NUM_ROWS 6
#define CLEVO_KEYBOARD_NUM_COLS 21
#define CLEVO_KEYBOARD_NUM_LEDS (CLEVO_KEYBOARD_NUM_ROWS * CLEVO_KEYBOARD_NUM_COLS)
/*---------------------------------------------------------*\
| ITE 829x keyboard defines (PID 0x8910) |
| 6 bytes with report ID 0xCC, per-key color commands |
| Protocol: [0xCC, cmd, d0, d1, d2, d3] |
| LED ID: ((row & 0x07) << 5) | (col & 0x1F) |
\*---------------------------------------------------------*/
#define CLEVO_829X_REPORT_ID 0xCC
#define CLEVO_829X_REPORT_SIZE 6
#define CLEVO_829X_NUM_ROWS 6
#define CLEVO_829X_NUM_COLS 20
#define CLEVO_829X_NUM_LEDS (CLEVO_829X_NUM_ROWS * CLEVO_829X_NUM_COLS)
#define CLEVO_829X_BRIGHTNESS_MAX 0x0A
#define CLEVO_829X_LED_ID(row, col) (unsigned char)((((row) & 0x07) << 5) | ((col) & 0x1F))
#define CLEVO_829X_CMD_SET_LED 0x01
#define CLEVO_829X_CMD_BRIGHTNESS 0x09
#define CLEVO_829X_CMD_ANIM 0x00
#define CLEVO_829X_CMD_CLEAR 0x0C
#define CLEVO_829X_CMD_WAVE_COLOR 0x15
#define CLEVO_829X_CMD_SNAKE_COLOR 0x16
#define CLEVO_829X_CMD_SCAN_COLOR 0x17
#define CLEVO_829X_CMD_RANDOM_COLOR 0x18
#define CLEVO_829X_COLOR_CUSTOM 0xAA
#define CLEVO_829X_COLOR_SLOT_BASE 0xA1
#define CLEVO_829X_PRESET_SLOT_BASE 0x71
#define CLEVO_829X_CMD_SAVE_BIOS 0x20
#define CLEVO_829X_CMD_STOP_ANIM 0x03
/*---------------------------------------------------------*\
| Shared defines |
\*---------------------------------------------------------*/
#define CLEVO_KEYBOARD_BRIGHTNESS_MIN 0x00
#define CLEVO_KEYBOARD_SPEED_MIN 0x00
#define CLEVO_KEYBOARD_SPEED_MAX 0x0A
#define CLEVO_KEYBOARD_SPEED_DEFAULT 0x00
#define CLEVO_KEYBOARD_BRIGHTNESS_MAX 0x32
/*---------------------------------------------------------*\
| ITE 8291 modes |
\*---------------------------------------------------------*/
#define CLEVO_KEYBOARD_SPEED_MIN 0x01
#define CLEVO_KEYBOARD_SPEED_MAX 0x0A
/*-----------------------------------------------------*\
| ITE 8291 modes |
\*-----------------------------------------------------*/
enum
{
CLEVO_KEYBOARD_MODE_DIRECT = 0x33,
@@ -97,36 +47,9 @@ enum
CLEVO_KEYBOARD_MODE_SPARK = 0x11,
};
/*---------------------------------------------------------*\
| ITE 829x firmware modes |
| Two command styles: |
| cmd=0x00, d0=mode_id (animation select) |
| cmd=mode_id, d0=0x00 (standalone effects) |
\*---------------------------------------------------------*/
enum
{
CLEVO_829X_MODE_DIRECT = 0xFD,
CLEVO_829X_MODE_WAVE = 0xA0,
CLEVO_829X_MODE_WAVE_COLOR = 0xB0,
CLEVO_829X_MODE_BREATH = 0xA1,
CLEVO_829X_MODE_BREATH_COLOR = 0xB1,
CLEVO_829X_MODE_BLINK = 0xA2,
CLEVO_829X_MODE_BLINK_COLOR = 0xB2,
CLEVO_829X_MODE_RANDOM = 0xA3,
CLEVO_829X_MODE_RANDOM_COLOR = 0xA9,
CLEVO_829X_MODE_SCAN = 0xA4,
/* Ripple (0x07) requires Insyde BIOS EC config, not supported */
CLEVO_829X_MODE_SNAKE = 0xA6,
CLEVO_829X_MODE_SNAKE_COLOR = 0xB6,
CLEVO_829X_MODE_RAINBOW_CYCLE = 0xA7,
CLEVO_829X_MODE_RAINBOW_GRAD = 0xA8,
CLEVO_829X_MODE_FN_COLOR = 0xAF,
CLEVO_829X_MODE_OFF = 0xFF,
};
/*---------------------------------------------------------*\
| Wave/reactive behaviour |
\*---------------------------------------------------------*/
/*-----------------------------------------------------*\
| Wave/reactive behaviour |
\*-----------------------------------------------------*/
enum
{
CLEVO_KEYBOARD_DIRECTION_LEFT = 0x01,
@@ -144,40 +67,23 @@ enum
class ClevoKeyboardController
{
public:
ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info, clevo_keyboard_type kb_type);
ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info);
~ClevoKeyboardController();
clevo_keyboard_type GetType();
int GetNumRows();
int GetNumCols();
int GetNumLEDs();
unsigned char GetBrightnessMax();
std::string GetDeviceLocation();
std::string GetSerialString();
std::string GetFirmwareVersion();
void TurnOff();
void SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char behaviour);
void SetModeColor(unsigned char color_idx, RGBColor color);
void SendColors(const unsigned char* color_data, unsigned char brightness, unsigned char speed);
void SetEffect829x(unsigned char mode_value, const std::vector<RGBColor>& colors, unsigned int direction = 0, unsigned int color_mode = 0);
void SetBrightnessSpeed829x(unsigned char brightness, unsigned char speed);
void SaveToBios829x();
void ClearBiosSave829x();
void StopAnimation829x();
void SetModeColor(unsigned char color_idx, unsigned char red, unsigned char green, unsigned char blue);
void SendColors(unsigned char* color_data, unsigned char brightness);
private:
hid_device* dev;
clevo_keyboard_type type;
std::string location;
unsigned short version;
unsigned char current_mode_829x = 0xFF;
unsigned char current_brightness_829x = 0xFF;
unsigned char current_speed_829x = 0xFF;
hid_device* dev;
std::string location;
unsigned short version;
void WriteControl8291(unsigned char* data);
void WriteControl(unsigned char* data);
void WriteRowData(unsigned char* data);
void SendCommand829x(unsigned char cmd, unsigned char d0 = 0, unsigned char d1 = 0, unsigned char d2 = 0, unsigned char d3 = 0);
void SendColorSlot829x(unsigned char cmd, unsigned char slot, RGBColor color);
};
@@ -1,11 +1,9 @@
/*---------------------------------------------------------*\
| ClevoKeyboardControllerDetect.cpp |
| |
| Detector for Clevo per-key RGB keyboard |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| Detector for Clevo per-key RGB keyboard (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 16 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -24,12 +22,11 @@
/*---------------------------------------------------------*\
| Clevo Keyboard product IDs |
| ITE 8291 and ITE 829x per-key RGB keyboard controllers |
| These are ITE 8291 per-key RGB keyboard controllers |
\*---------------------------------------------------------*/
#define CLEVO_KEYBOARD_PID_8291 0x600B
#define CLEVO_KEYBOARD_PID_829X 0x8910
#define CLEVO_KEYBOARD_PID_600B 0x600B
static DetectedControllers DetectClevoKeyboard(hid_device_info* info, clevo_keyboard_type kb_type)
DetectedControllers DetectClevoKeyboardControllers(hid_device_info* info, const std::string& /*name*/)
{
DetectedControllers detected_controllers;
hid_device* dev;
@@ -38,7 +35,7 @@ static DetectedControllers DetectClevoKeyboard(hid_device_info* info, clevo_keyb
if(dev)
{
ClevoKeyboardController* controller = new ClevoKeyboardController(dev, *info, kb_type);
ClevoKeyboardController* controller = new ClevoKeyboardController(dev, *info);
RGBController_ClevoKeyboard* rgb_controller = new RGBController_ClevoKeyboard(controller);
detected_controllers.push_back(rgb_controller);
@@ -47,15 +44,4 @@ static DetectedControllers DetectClevoKeyboard(hid_device_info* info, clevo_keyb
return(detected_controllers);
}
DetectedControllers DetectClevoKeyboard8291(hid_device_info* info, const std::string& /*name*/)
{
return(DetectClevoKeyboard(info, CLEVO_KB_ITE8291));
}
DetectedControllers DetectClevoKeyboard829x(hid_device_info* info, const std::string& /*name*/)
{
return(DetectClevoKeyboard(info, CLEVO_KB_ITE829X));
}
REGISTER_HID_DETECTOR_PU("Clevo Keyboard (ITE 8291)", DetectClevoKeyboard8291, ITE_VID, CLEVO_KEYBOARD_PID_8291, 0xFF03, 0x01);
REGISTER_HID_DETECTOR_PU("Clevo Keyboard (ITE 829x)", DetectClevoKeyboard829x, ITE_VID, CLEVO_KEYBOARD_PID_829X, 0xFF89, 0xCC);
REGISTER_HID_DETECTOR_PU("CLEVO Keyboard", DetectClevoKeyboardControllers, 0x048D, 0x600B, 0xFF03, 0x01);
@@ -1,11 +1,9 @@
/*---------------------------------------------------------*\
| ClevoKeyboardDevices.cpp |
| |
| Device list for Clevo per-key RGB keyboards |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| Device list for Clevo per-key RGB keyboards (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 21 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -114,48 +112,6 @@ static const std::vector<unsigned int> clevo_tkl_values =
15, // Right
};
/*---------------------------------------------------------*\
| ITE 829x LED value mapping |
| LED ID = ((row & 7) << 5) | col |
| Row 5 (F-keys) = 0xA0+, Row 4 (numbers) = 0x80+ |
| Row 3 (QWERTY) = 0x60+, Row 2 (home) = 0x40+ |
| Row 1 (Z row) = 0x20+, Row 0 (mods) = 0x00+ |
\*---------------------------------------------------------*/
static const std::vector<unsigned int> clevo_829x_full_values =
{
/*---------------------------------------------------------*\
| Values in FULL layout visual order (same as CM keyboard) |
| LED ID = ((row & 7) << 5) | col |
| Row counts: 16, 21, 21, 17, 18, 13 |
\*---------------------------------------------------------*/
/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0, 0,
/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC INS HOME PGUP NMLK NMDV NMTM NMMI */
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2D, 0x2E, 0x0E, 0x10, 0x12, 0x30, 0x31, 0x32, 0x33,
/* TAB Q W E R T Y U I O P [ ] ANSI\ DEL END PGDN NM7 NM8 NM9 NMPL */
0x40, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x0F, 0x11, 0x13, 0x50, 0x51, 0x52, 0x53,
/* CPLK A S D F G H J K L ; ' # ENTR NM4 NM5 NM6 */
0x60, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0, 0x6E, 0x70, 0x71, 0x72,
/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */
0x80, 0, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8F, 0x90, 0x91, 0x92, 0x93,
/* LCTL LFNC LWIN LALT SPACE RALT RMNU RCTL ARWL ARDN ARWR NM0 NMPD */
0xA0, 0xA2, 0xA3, 0xA4, 0xA5, 0xAA, 0xAB, 0xAC, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2,
};
keyboard_keymap_overlay_values clevo_829x_keyboard_layout
{
KEYBOARD_SIZE_FULL,
{
clevo_829x_full_values,
{
/* No regional overlays needed */
}
},
{
/* No edit keys needed - all keys defined in full layout */
}
};
keyboard_keymap_overlay_values clevo_keyboard_layout
{
KEYBOARD_SIZE_TKL,
@@ -166,9 +122,10 @@ keyboard_keymap_overlay_values clevo_keyboard_layout
}
},
{
/*---------------------------------------------------------*\
| Edit Keys |
\*---------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------*\
| Edit Keys |
| Zone, Row, Column, Value, Key, Alternate Name, OpCode, |
\*-------------------------------------------------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------*\
| Remove keys not present on Clevo keyboard |
@@ -1,11 +1,9 @@
/*---------------------------------------------------------*\
| ClevoKeyboardDevices.h |
| |
| Device list for Clevo per-key RGB keyboards |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| Device list for Clevo per-key RGB keyboards (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 21 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -16,8 +14,7 @@
#include <string>
#include "KeyboardLayoutManager.h"
/*---------------------------------------------------------*\
| Clevo keyboard layout definitions |
\*---------------------------------------------------------*/
/*-----------------------------------------------------*\
| Clevo keyboard layout definitions |
\*-----------------------------------------------------*/
extern keyboard_keymap_overlay_values clevo_keyboard_layout;
extern keyboard_keymap_overlay_values clevo_829x_keyboard_layout;
@@ -1,17 +1,14 @@
/*---------------------------------------------------------*\
| RGBController_ClevoKeyboard.cpp |
| |
| RGBController for Clevo per-key RGB keyboard |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| RGBController for Clevo per-key RGB keyboard (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 16 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <vector>
#include "RGBController_ClevoKeyboard.h"
#include "KeyboardLayoutManager.h"
@@ -43,306 +40,152 @@ RGBController_ClevoKeyboard::RGBController_ClevoKeyboard(ClevoKeyboardController
Direct.value = CLEVO_KEYBOARD_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Direct.brightness_max = controller->GetBrightnessMax();
Direct.brightness = controller->GetBrightnessMax();
Direct.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Direct.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
/*---------------------------------------------------------*\
| ITE 8291 built-in effects (not available on ITE 829x) |
\*---------------------------------------------------------*/
if(controller->GetType() == CLEVO_KB_ITE8291)
{
mode Rainbow;
Rainbow.name = "Rainbow";
Rainbow.value = CLEVO_KEYBOARD_MODE_RAINBOW;
Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
Rainbow.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Rainbow.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Rainbow.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Rainbow.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Rainbow.brightness_max = controller->GetBrightnessMax();
Rainbow.brightness = controller->GetBrightnessMax();
Rainbow.color_mode = MODE_COLORS_NONE;
modes.push_back(Rainbow);
mode Rainbow;
Rainbow.name = "Rainbow";
Rainbow.value = CLEVO_KEYBOARD_MODE_RAINBOW;
Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
Rainbow.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Rainbow.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Rainbow.speed = 0x05;
Rainbow.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Rainbow.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Rainbow.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Rainbow.color_mode = MODE_COLORS_NONE;
modes.push_back(Rainbow);
mode Wave;
Wave.name = "Wave";
Wave.value = CLEVO_KEYBOARD_MODE_WAVE;
Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Wave.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Wave.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Wave.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Wave.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Wave.brightness_max = controller->GetBrightnessMax();
Wave.brightness = controller->GetBrightnessMax();
Wave.direction = MODE_DIRECTION_LEFT;
Wave.colors_min = 1;
Wave.colors_max = 1;
Wave.colors.resize(1);
Wave.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Wave);
mode Wave;
Wave.name = "Wave";
Wave.value = CLEVO_KEYBOARD_MODE_WAVE;
Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Wave.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Wave.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Wave.speed = 0x05;
Wave.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Wave.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Wave.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Wave.direction = MODE_DIRECTION_LEFT;
Wave.colors_min = 1;
Wave.colors_max = 1;
Wave.colors.resize(1);
Wave.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Wave);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = CLEVO_KEYBOARD_MODE_BREATH;
Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Breathing.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Breathing.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Breathing.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Breathing.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Breathing.brightness_max = controller->GetBrightnessMax();
Breathing.brightness = controller->GetBrightnessMax();
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.colors.resize(1);
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Breathing);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = CLEVO_KEYBOARD_MODE_BREATH;
Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Breathing.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Breathing.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Breathing.speed = 0x05;
Breathing.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Breathing.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Breathing.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.colors.resize(1);
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Breathing);
mode Reactive;
Reactive.name = "Reactive";
Reactive.value = CLEVO_KEYBOARD_MODE_REACTIVE;
Reactive.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Reactive.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Reactive.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Reactive.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Reactive.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Reactive.brightness_max = controller->GetBrightnessMax();
Reactive.brightness = controller->GetBrightnessMax();
Reactive.colors_min = 1;
Reactive.colors_max = 1;
Reactive.colors.resize(1);
Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Reactive);
mode Reactive;
Reactive.name = "Reactive";
Reactive.value = CLEVO_KEYBOARD_MODE_REACTIVE;
Reactive.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Reactive.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Reactive.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Reactive.speed = 0x05;
Reactive.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Reactive.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Reactive.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Reactive.colors_min = 1;
Reactive.colors_max = 1;
Reactive.colors.resize(1);
Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Reactive);
mode Ripple;
Ripple.name = "Ripple";
Ripple.value = CLEVO_KEYBOARD_MODE_RIPPLE;
Ripple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Ripple.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Ripple.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Ripple.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Ripple.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Ripple.brightness_max = controller->GetBrightnessMax();
Ripple.brightness = controller->GetBrightnessMax();
Ripple.colors_min = 1;
Ripple.colors_max = 1;
Ripple.colors.resize(1);
Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Ripple);
mode Ripple;
Ripple.name = "Ripple";
Ripple.value = CLEVO_KEYBOARD_MODE_RIPPLE;
Ripple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Ripple.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Ripple.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Ripple.speed = 0x05;
Ripple.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Ripple.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Ripple.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Ripple.colors_min = 1;
Ripple.colors_max = 1;
Ripple.colors.resize(1);
Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Ripple);
mode Marquee;
Marquee.name = "Marquee";
Marquee.value = CLEVO_KEYBOARD_MODE_MARQUEE;
Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Marquee.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Marquee.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Marquee.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Marquee.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Marquee.brightness_max = controller->GetBrightnessMax();
Marquee.brightness = controller->GetBrightnessMax();
Marquee.colors_min = 1;
Marquee.colors_max = 1;
Marquee.colors.resize(1);
Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Marquee);
mode Marquee;
Marquee.name = "Marquee";
Marquee.value = CLEVO_KEYBOARD_MODE_MARQUEE;
Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Marquee.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Marquee.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Marquee.speed = 0x05;
Marquee.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Marquee.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Marquee.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Marquee.colors_min = 1;
Marquee.colors_max = 1;
Marquee.colors.resize(1);
Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Marquee);
mode Raindrop;
Raindrop.name = "Raindrop";
Raindrop.value = CLEVO_KEYBOARD_MODE_RAINDROP;
Raindrop.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Raindrop.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Raindrop.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Raindrop.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Raindrop.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Raindrop.brightness_max = controller->GetBrightnessMax();
Raindrop.brightness = controller->GetBrightnessMax();
Raindrop.colors_min = 1;
Raindrop.colors_max = 1;
Raindrop.colors.resize(1);
Raindrop.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Raindrop);
mode Raindrop;
Raindrop.name = "Raindrop";
Raindrop.value = CLEVO_KEYBOARD_MODE_RAINDROP;
Raindrop.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Raindrop.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Raindrop.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Raindrop.speed = 0x05;
Raindrop.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Raindrop.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Raindrop.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Raindrop.colors_min = 1;
Raindrop.colors_max = 1;
Raindrop.colors.resize(1);
Raindrop.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Raindrop);
mode Aurora;
Aurora.name = "Aurora";
Aurora.value = CLEVO_KEYBOARD_MODE_AURORA;
Aurora.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Aurora.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Aurora.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Aurora.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Aurora.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Aurora.brightness_max = controller->GetBrightnessMax();
Aurora.brightness = controller->GetBrightnessMax();
Aurora.colors_min = 1;
Aurora.colors_max = 1;
Aurora.colors.resize(1);
Aurora.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Aurora);
mode Aurora;
Aurora.name = "Aurora";
Aurora.value = CLEVO_KEYBOARD_MODE_AURORA;
Aurora.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Aurora.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Aurora.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Aurora.speed = 0x05;
Aurora.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Aurora.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Aurora.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Aurora.colors_min = 1;
Aurora.colors_max = 1;
Aurora.colors.resize(1);
Aurora.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Aurora);
mode Spark;
Spark.name = "Spark";
Spark.value = CLEVO_KEYBOARD_MODE_SPARK;
Spark.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Spark.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Spark.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Spark.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Spark.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Spark.brightness_max = controller->GetBrightnessMax();
Spark.brightness = controller->GetBrightnessMax();
Spark.colors_min = 1;
Spark.colors_max = 1;
Spark.colors.resize(1);
Spark.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Spark);
}
/*---------------------------------------------------------*\
| ITE 829x firmware effects |
\*---------------------------------------------------------*/
if(controller->GetType() == CLEVO_KB_ITE829X)
{
unsigned char bmax = controller->GetBrightnessMax();
mode Wave;
Wave.name = "Wave";
Wave.value = CLEVO_829X_MODE_WAVE;
Wave.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_DIRECTION_DIAG;
Wave.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Wave.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Wave.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Wave.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Wave.brightness_max = bmax;
Wave.brightness = bmax;
Wave.direction = MODE_DIRECTION_LEFT;
Wave.colors_min = 1;
Wave.colors_max = 1;
Wave.colors.resize(1, ToRGBColor(255, 0, 0));
Wave.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Wave);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = CLEVO_829X_MODE_BREATH;
Breathing.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Breathing.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Breathing.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Breathing.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Breathing.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Breathing.brightness_max = bmax;
Breathing.brightness = bmax;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.colors.resize(1, ToRGBColor(255, 0, 0));
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Breathing);
mode Flashing;
Flashing.name = "Flashing";
Flashing.value = CLEVO_829X_MODE_BLINK;
Flashing.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Flashing.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Flashing.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Flashing.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Flashing.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Flashing.brightness_max = bmax;
Flashing.brightness = bmax;
Flashing.colors_min = 1;
Flashing.colors_max = 1;
Flashing.colors.resize(1, ToRGBColor(255, 0, 0));
Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Flashing);
mode Random;
Random.name = "Random";
Random.value = CLEVO_829X_MODE_RANDOM;
Random.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Random.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Random.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Random.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Random.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Random.brightness_max = bmax;
Random.brightness = bmax;
Random.colors_min = 1;
Random.colors_max = 1;
Random.colors.resize(1, ToRGBColor(255, 0, 0));
Random.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Random);
mode Scan;
Scan.name = "Scan";
Scan.value = CLEVO_829X_MODE_SCAN;
Scan.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Scan.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Scan.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Scan.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Scan.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Scan.brightness_max = bmax;
Scan.brightness = bmax;
Scan.colors_min = 2;
Scan.colors_max = 2;
Scan.colors.resize(2);
Scan.colors[0] = ToRGBColor(255, 0, 0);
Scan.colors[1] = ToRGBColor(0, 0, 255);
Scan.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Scan);
mode Snake;
Snake.name = "Snake";
Snake.value = CLEVO_829X_MODE_SNAKE;
Snake.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_DIAG;
Snake.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Snake.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Snake.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Snake.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Snake.brightness_max = bmax;
Snake.brightness = bmax;
Snake.direction = MODE_DIRECTION_UP_LEFT;
Snake.colors_min = 1;
Snake.colors_max = 1;
Snake.colors.resize(1, ToRGBColor(255, 0, 0));
Snake.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Snake);
mode RainbowCycle;
RainbowCycle.name = "Spectrum Cycle";
RainbowCycle.value = CLEVO_829X_MODE_RAINBOW_CYCLE;
RainbowCycle.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
RainbowCycle.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
RainbowCycle.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
RainbowCycle.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
RainbowCycle.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
RainbowCycle.brightness_max = bmax;
RainbowCycle.brightness = bmax;
RainbowCycle.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowCycle);
mode RainbowGrad;
RainbowGrad.name = "Rainbow Wave";
RainbowGrad.value = CLEVO_829X_MODE_RAINBOW_GRAD;
RainbowGrad.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_DIRECTION_DIAG;
RainbowGrad.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
RainbowGrad.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
RainbowGrad.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
RainbowGrad.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
RainbowGrad.brightness_max = bmax;
RainbowGrad.brightness = bmax;
RainbowGrad.direction = MODE_DIRECTION_LEFT;
RainbowGrad.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowGrad);
mode FnColor;
FnColor.name = "Fn Highlight";
FnColor.value = CLEVO_829X_MODE_FN_COLOR;
FnColor.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
FnColor.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
FnColor.brightness_max = bmax;
FnColor.brightness = bmax;
FnColor.colors_min = 1;
FnColor.colors_max = 1;
FnColor.colors.resize(1, ToRGBColor(255, 0, 0));
FnColor.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(FnColor);
}
mode Spark;
Spark.name = "Spark";
Spark.value = CLEVO_KEYBOARD_MODE_SPARK;
Spark.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Spark.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Spark.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Spark.speed = 0x05;
Spark.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Spark.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Spark.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Spark.colors_min = 1;
Spark.colors_max = 1;
Spark.colors.resize(1);
Spark.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Spark);
mode Off;
Off.name = "Off";
@@ -364,22 +207,16 @@ RGBController_ClevoKeyboard::~RGBController_ClevoKeyboard()
void RGBController_ClevoKeyboard::SetupZones()
{
/*---------------------------------------------------------*\
| Select layout based on device type |
| Create keyboard layout using KeyboardLayoutManager |
\*---------------------------------------------------------*/
bool is_829x = (controller->GetType() == CLEVO_KB_ITE829X);
keyboard_keymap_overlay_values& layout = is_829x
? clevo_829x_keyboard_layout
: clevo_keyboard_layout;
KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ISO_QWERTY,
layout.base_size,
layout.key_values);
clevo_keyboard_layout.base_size,
clevo_keyboard_layout.key_values);
new_kb.ChangeKeys(layout);
new_kb.ChangeKeys(clevo_keyboard_layout);
/*---------------------------------------------------------*\
| Create keyboard zone from KLM |
| Create a matrix zone for the keyboard |
\*---------------------------------------------------------*/
zone keyboard_zone;
@@ -396,6 +233,9 @@ void RGBController_ClevoKeyboard::SetupZones()
zones.push_back(keyboard_zone);
/*---------------------------------------------------------*\
| Create LEDs from the KeyboardLayoutManager data |
\*---------------------------------------------------------*/
for(unsigned int led_idx = 0; led_idx < keyboard_zone.leds_count; led_idx++)
{
led new_led;
@@ -409,48 +249,36 @@ void RGBController_ClevoKeyboard::SetupZones()
SetupColors();
/*---------------------------------------------------------*\
| Build buffer map from LED values to color pointers |
| 829x LED IDs have gaps (stride 32), need larger map |
| Create buffer map to translate OpenRGB LED order to |
| hardware LED order. The hardware expects 126 color values |
| indexed by LED position (0-125). |
\*---------------------------------------------------------*/
null_color = 0x00000000;
size_t map_size = is_829x
? CLEVO_829X_LED_ID(CLEVO_829X_NUM_ROWS - 1, CLEVO_829X_NUM_COLS - 1) + 1
: (size_t)controller->GetNumLEDs();
buffer_map.resize(map_size, &null_color);
buffer_map.resize(CLEVO_KEYBOARD_NUM_LEDS, &null_color);
for(size_t led_idx = 0; led_idx < leds.size(); led_idx++)
{
if(leds[led_idx].value < buffer_map.size())
{
buffer_map[leds[led_idx].value] = &colors[led_idx];
}
buffer_map[leds[led_idx].value] = &colors[led_idx];
}
}
void RGBController_ClevoKeyboard::DeviceUpdateLEDs()
{
int num_rows = controller->GetNumRows();
int num_cols = controller->GetNumCols();
bool is_829x = (controller->GetType() == CLEVO_KB_ITE829X);
/*---------------------------------------------------------*\
| Build color data array using buffer map and send to device|
| The buffer_map translates from hardware LED index to the |
| corresponding color pointer for that LED. |
\*---------------------------------------------------------*/
unsigned char color_data[CLEVO_KEYBOARD_NUM_LEDS * 3];
std::vector<unsigned char> color_data(num_rows * num_cols * 3);
for(int row = 0; row < num_rows; row++)
for(int i = 0; i < CLEVO_KEYBOARD_NUM_LEDS; i++)
{
for(int col = 0; col < num_cols; col++)
{
int seq = row * num_cols + col;
int led_id = is_829x ? CLEVO_829X_LED_ID(row, col) : seq;
color_data[seq * 3 + 0] = RGBGetRValue(*buffer_map[led_id]);
color_data[seq * 3 + 1] = RGBGetGValue(*buffer_map[led_id]);
color_data[seq * 3 + 2] = RGBGetBValue(*buffer_map[led_id]);
}
color_data[i * 3 + 0] = RGBGetRValue(*buffer_map[i]);
color_data[i * 3 + 1] = RGBGetGValue(*buffer_map[i]);
color_data[i * 3 + 2] = RGBGetBValue(*buffer_map[i]);
}
controller->SendColors(color_data.data(), modes[active_mode].brightness, modes[active_mode].speed);
controller->SendColors(color_data, modes[active_mode].brightness);
}
void RGBController_ClevoKeyboard::DeviceUpdateZoneLEDs(int /*zone*/)
@@ -476,16 +304,6 @@ void RGBController_ClevoKeyboard::DeviceUpdateMode()
return;
}
/*---------------------------------------------------------*\
| Handle ITE 829x firmware effects |
\*---------------------------------------------------------*/
if(controller->GetType() == CLEVO_KB_ITE829X && mode_value != CLEVO_KEYBOARD_MODE_DIRECT)
{
controller->SetEffect829x(mode_value, modes[active_mode].colors, modes[active_mode].direction, modes[active_mode].color_mode);
controller->SetBrightnessSpeed829x(modes[active_mode].brightness, modes[active_mode].speed);
return;
}
/*---------------------------------------------------------*\
| Handle Direct (per-key) mode |
\*---------------------------------------------------------*/
@@ -500,26 +318,41 @@ void RGBController_ClevoKeyboard::DeviceUpdateMode()
\*---------------------------------------------------------*/
unsigned char brightness = modes[active_mode].brightness;
unsigned char speed = modes[active_mode].speed;
unsigned char behaviour = (mode_value == CLEVO_KEYBOARD_MODE_WAVE)
? modes[active_mode].direction + 1
: 0x00;
unsigned char behaviour = 0x00;
/*---------------------------------------------------------*\
| Set direction for wave mode |
\*---------------------------------------------------------*/
if(mode_value == CLEVO_KEYBOARD_MODE_WAVE)
{
switch(modes[active_mode].direction)
{
case MODE_DIRECTION_LEFT:
behaviour = CLEVO_KEYBOARD_DIRECTION_LEFT;
break;
case MODE_DIRECTION_RIGHT:
behaviour = CLEVO_KEYBOARD_DIRECTION_RIGHT;
break;
case MODE_DIRECTION_UP:
behaviour = CLEVO_KEYBOARD_DIRECTION_UP;
break;
case MODE_DIRECTION_DOWN:
behaviour = CLEVO_KEYBOARD_DIRECTION_DOWN;
break;
}
}
/*---------------------------------------------------------*\
| Set mode color if applicable |
\*---------------------------------------------------------*/
if(modes[active_mode].colors.size() > 0)
{
controller->SetModeColor(1, modes[active_mode].colors[0]);
unsigned char red = RGBGetRValue(modes[active_mode].colors[0]);
unsigned char green = RGBGetGValue(modes[active_mode].colors[0]);
unsigned char blue = RGBGetBValue(modes[active_mode].colors[0]);
controller->SetModeColor(1, red, green, blue);
}
controller->SetMode(mode_value, brightness, speed, behaviour);
}
void RGBController_ClevoKeyboard::DeviceSaveMode()
{
if(controller->GetType() == CLEVO_KB_ITE829X)
{
DeviceUpdateMode();
controller->SaveToBios829x();
}
}
@@ -1,11 +1,9 @@
/*---------------------------------------------------------*\
| RGBController_ClevoKeyboard.h |
| |
| RGBController for Clevo per-key RGB keyboard |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| RGBController for Clevo per-key RGB keyboard (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 16 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -20,7 +18,7 @@
class RGBController_ClevoKeyboard : public RGBController
{
public:
explicit RGBController_ClevoKeyboard(ClevoKeyboardController* controller_ptr);
RGBController_ClevoKeyboard(ClevoKeyboardController* controller_ptr);
~RGBController_ClevoKeyboard();
void SetupZones();
@@ -30,7 +28,6 @@ public:
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
ClevoKeyboardController* controller;
@@ -71,7 +71,6 @@ REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Ti SUPER Advanced OC-V", Detec
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Ti SUPER Ultra W", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070TI_SUPER_ULTRA_W, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 SUPER Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070_SUPER_ULTRA_W_OC, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Vulcan OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070_VULCAN_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070_ULTRAW_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 SUPER Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070S_ULTRAW_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4080_ULTRAW_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4080_ULTRAW_OCV2, 0x61);
@@ -411,7 +411,6 @@ REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White OC",
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_O24G_GAMING_WHITE_2, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG MATRIX PLATINUM GeForce RTX 4090", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_MATRIX_PLATINUM_RTX_4090_24G, 0x67);
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 5060 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5060TI_O16G_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 BTF White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING_BTF_WHITE, 0x67);
@@ -1,205 +0,0 @@
/*---------------------------------------------------------*\
| EdifierHaloController.cpp |
| |
| Driver for Edifier Halo series USB/HID devices |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include "LogManager.h"
#include "EdifierHaloController.h"
#include "StringUtils.h"
EdifierHaloController::EdifierHaloController(hid_device* dev_handle, const char* path)
{
dev = dev_handle;
location = path;
wchar_t name_string[256];
int result;
result = hid_get_manufacturer_string(dev, name_string, 256);
if(result == 0)
{
device_name = StringUtils::wstring_to_string(name_string);
}
else
{
device_name = "Edifier";
LOG_WARNING("[EdifierHaloController] Failed to read manufacturer string for %s", path);
}
result = hid_get_product_string(dev, name_string, 256);
if(result == 0)
{
device_name.append(" ").append(StringUtils::wstring_to_string(name_string));
}
else
{
device_name.append(" Halo PixelBar");
LOG_WARNING("[EdifierHaloController] Failed to read product string for %s", path);
}
wchar_t serial_string[128];
result = hid_get_serial_number_string(dev, serial_string, 128);
if(result == 0)
{
serial = StringUtils::wstring_to_string(serial_string);
}
else
{
serial = "";
LOG_DEBUG("[EdifierHaloController] No serial number available for %s", path);
}
}
EdifierHaloController::~EdifierHaloController()
{
if(dev)
{
hid_close(dev);
}
}
std::string EdifierHaloController::GetDeviceName() const
{
return(device_name);
}
std::string EdifierHaloController::GetSerial() const
{
return(serial);
}
std::string EdifierHaloController::GetLocation() const
{
return("HID: " + location);
}
void EdifierHaloController::SetAmbientLightPower(bool enabled)
{
unsigned char mode = enabled ? 0x01 : 0x00;
unsigned char payload[] =
{
0x00,
0x00,
0x00,
0x00,
mode,
0xFF,
0xFF
};
SendEdifierPacket(0x6b, payload, sizeof(payload));
}
void EdifierHaloController::SetAmbientLight(unsigned char effect, unsigned char r, unsigned char g, unsigned char b, unsigned char brightness, unsigned char speed)
{
unsigned char raw_brightness = 0x3c;
if (brightness == 1) raw_brightness = 0x14;
else if (brightness == 2) raw_brightness = 0x28;
else if (brightness == 3) raw_brightness = 0x3c;
if (speed < 1) speed = 1;
if (speed > 10) speed = 10;
unsigned char payload[] =
{
0x13,
effect,
r,
g,
b,
raw_brightness,
speed
};
SendEdifierPacket(0x6b, payload, sizeof(payload));
}
void EdifierHaloController::SetPixelScreenColor(unsigned char r, unsigned char g, unsigned char b)
{
unsigned char payload[] =
{
0x03,
r,
g,
b
};
SendEdifierPacket(0xef, payload, sizeof(payload));
}
void EdifierHaloController::SetScreenUIModel(unsigned char model_id, unsigned char r, unsigned char g, unsigned char b)
{
unsigned char payload[] =
{
0x02,
r,
g,
b,
0x00,
0x01,
model_id,
0xff,
0xff
};
SendEdifierPacket(0xef, payload, sizeof(payload));
}
void EdifierHaloController::SetScreenScene(unsigned char category, unsigned char index, unsigned char r, unsigned char g, unsigned char b)
{
unsigned char payload[] =
{
0x01, // group
r,
g,
b,
0x00,
0x01,
category,
index,
0xff // option
};
SendEdifierPacket(0xef, payload, sizeof(payload));
}
void EdifierHaloController::SendEdifierPacket(unsigned char commandIndex, const unsigned char* payload, unsigned int payload_size)
{
if(!dev)
{
return;
}
unsigned char packet[64];
memset(packet, 0x00, sizeof(packet));
packet[0] = 0x2e;
packet[1] = 0xaa;
packet[2] = 0xec;
packet[3] = commandIndex;
packet[4] = (unsigned char)((payload_size >> 8) & 0xff);
packet[5] = (unsigned char)(payload_size & 0xff);
if(payload_size > 0 && payload != nullptr)
{
memcpy(&packet[6], payload, payload_size);
}
// Calculate checksum
unsigned int sum = 0;
for(unsigned int i = 1; i < 6 + payload_size; i++)
{
sum += packet[i];
}
packet[6 + payload_size] = (unsigned char)(sum & 0xff);
std::lock_guard<std::mutex> guard(device_mutex);
int bytes_written = hid_write(dev, packet, sizeof(packet));
if(bytes_written != sizeof(packet))
{
LOG_ERROR("[EdifierHaloController] Failed to write HID packet (%d/%d bytes): %ls",
bytes_written, (int)sizeof(packet), hid_error(dev));
}
}
@@ -1,64 +0,0 @@
/*---------------------------------------------------------*\
| EdifierHaloController.h |
| |
| Driver for Edifier Halo series USB/HID devices |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <mutex>
#include <string>
#include <hidapi.h>
enum
{
EDIFIER_HALO_AMBIENT_MODE_BREATHING = 1, // Breathing
EDIFIER_HALO_AMBIENT_MODE_TIDE = 2, // Rainbow
EDIFIER_HALO_AMBIENT_MODE_STATIC = 3, // Static
EDIFIER_HALO_AMBIENT_MODE_RIPPLE = 4, // Glittering and colorful
EDIFIER_HALO_AMBIENT_MODE_FLOW = 5, // Light-flow
EDIFIER_HALO_AMBIENT_MODE_DYNAMIC = 6 // Dynamic Shadows
};
enum
{
EDIFIER_HALO_AMBIENT_SPEED_SLOWEST = 1,
EDIFIER_HALO_AMBIENT_SPEED_FASTEST = 10
};
enum
{
EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST = 1,
EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST = 3
};
class EdifierHaloController
{
public:
EdifierHaloController(hid_device* dev_handle, const char* path);
~EdifierHaloController();
std::string GetDeviceName() const;
std::string GetSerial() const;
std::string GetLocation() const;
void SetAmbientLight(unsigned char effect, unsigned char r, unsigned char g, unsigned char b, unsigned char brightness, unsigned char speed);
void SetAmbientLightPower(bool enabled);
void SetPixelScreenColor(unsigned char r, unsigned char g, unsigned char b);
void SetScreenUIModel(unsigned char model_id, unsigned char r, unsigned char g, unsigned char b);
void SetScreenScene(unsigned char category, unsigned char index, unsigned char r, unsigned char g, unsigned char b);
private:
hid_device* dev;
std::string device_name;
std::string serial;
std::string location;
std::mutex device_mutex;
void SendEdifierPacket(unsigned char commandIndex, const unsigned char* payload, unsigned int payload_size);
};
@@ -1,56 +0,0 @@
/*---------------------------------------------------------*\
| EdifierHaloControllerDetect.cpp |
| |
| Detector for Edifier Halo PixelBar USB/HID devices |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include <memory>
#include "DetectionManager.h"
#include "LogManager.h"
#include "RGBController_EdifierHaloPixelbarAmbient.h"
#include "RGBController_EdifierHaloPixelbarScreen.h"
#include "EdifierHaloController.h"
/*---------------------------------------------------------*\
| Edifier HID vendor and product IDs |
\*---------------------------------------------------------*/
#define EDIFIER_VID 0x2D99
#define EDIFIER_PIXELBAR_PID 0xA106
#define EDIFIER_PIXELBAR_INTERFACE 4
#define EDIFIER_PIXELBAR_USAGE_PAGE 0xFF14
#define EDIFIER_PIXELBAR_USAGE 0x01
DetectedControllers DetectEdifierHaloControllers(hid_device_info* info, const std::string&)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
EdifierHaloController* controller = new EdifierHaloController(dev, info->path);
RGBController_EdifierHaloPixelbarAmbient* ambient_controller = new RGBController_EdifierHaloPixelbarAmbient(controller);
RGBController_EdifierHaloPixelbarScreen* screen_controller = new RGBController_EdifierHaloPixelbarScreen(controller);
detected_controllers.push_back(ambient_controller);
detected_controllers.push_back(screen_controller);
LOG_INFO("[EdifierHaloPixelbarDetect] Detected Edifier Halo PixelBar at %s (Instantiated Ambient & Screen controllers)", info->path);
}
else
{
LOG_WARNING("[EdifierHaloPixelbarDetect] Failed to open Edifier Halo PixelBar at %s", info->path);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Edifier Halo Pixelbar", DetectEdifierHaloControllers, EDIFIER_VID, EDIFIER_PIXELBAR_PID, EDIFIER_PIXELBAR_INTERFACE, EDIFIER_PIXELBAR_USAGE_PAGE, EDIFIER_PIXELBAR_USAGE);
@@ -1,182 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_EdifierHaloPixelbarAmbient.cpp |
| |
| RGBController for Edifier Halo PixelBar Ambient Light |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_EdifierHaloPixelbarAmbient.h"
/**------------------------------------------------------------------*\
@name Edifier Halo Pixelbar
@category Speaker
@type USB
@save :robot:
@direct :x:
@effects :white_check_mark:
@detectors DetectEdifierHaloControllers
@comment
\*-------------------------------------------------------------------*/
RGBController_EdifierHaloPixelbarAmbient::RGBController_EdifierHaloPixelbarAmbient(EdifierHaloController* controller_ptr)
{
controller = controller_ptr;
first_leds_update_ignored = false;
first_mode_update_ignored = false;
name = "Edifier Halo Pixelbar - Light Effects";
vendor = "Edifier";
type = DEVICE_TYPE_SPEAKER;
description = "Edifier Halo PixelBar Light Effects";
serial = controller->GetSerial();
location = controller->GetLocation();
// Setup modes
mode breathing_mode;
breathing_mode.name = "Breathing";
breathing_mode.value = EDIFIER_HALO_AMBIENT_MODE_BREATHING;
breathing_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
breathing_mode.color_mode = MODE_COLORS_PER_LED;
breathing_mode.speed_min = EDIFIER_HALO_AMBIENT_SPEED_SLOWEST;
breathing_mode.speed_max = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
breathing_mode.speed = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
breathing_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
breathing_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
breathing_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(breathing_mode);
mode tide_mode;
tide_mode.name = "Rainbow";
tide_mode.value = EDIFIER_HALO_AMBIENT_MODE_TIDE;
tide_mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
tide_mode.color_mode = MODE_COLORS_NONE;
tide_mode.speed_min = EDIFIER_HALO_AMBIENT_SPEED_SLOWEST;
tide_mode.speed_max = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
tide_mode.speed = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
tide_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
tide_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
tide_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(tide_mode);
mode static_mode;
static_mode.name = "Static";
static_mode.value = EDIFIER_HALO_AMBIENT_MODE_STATIC;
static_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
static_mode.color_mode = MODE_COLORS_MODE_SPECIFIC;
static_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
static_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
static_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(static_mode);
mode ripple_mode;
ripple_mode.name = "Glittering and colorful";
ripple_mode.value = EDIFIER_HALO_AMBIENT_MODE_RIPPLE;
ripple_mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
ripple_mode.color_mode = MODE_COLORS_NONE;
ripple_mode.speed_min = EDIFIER_HALO_AMBIENT_SPEED_SLOWEST;
ripple_mode.speed_max = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
ripple_mode.speed = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
ripple_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
ripple_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
ripple_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(ripple_mode);
mode flow_mode;
flow_mode.name = "Light-flow";
flow_mode.value = EDIFIER_HALO_AMBIENT_MODE_FLOW;
flow_mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
flow_mode.color_mode = MODE_COLORS_NONE;
flow_mode.speed_min = EDIFIER_HALO_AMBIENT_SPEED_SLOWEST;
flow_mode.speed_max = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
flow_mode.speed = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
flow_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
flow_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
flow_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(flow_mode);
mode dynamic_mode;
dynamic_mode.name = "Dynamic Shadows";
dynamic_mode.value = EDIFIER_HALO_AMBIENT_MODE_DYNAMIC;
dynamic_mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
dynamic_mode.color_mode = MODE_COLORS_NONE;
dynamic_mode.speed_min = EDIFIER_HALO_AMBIENT_SPEED_SLOWEST;
dynamic_mode.speed_max = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
dynamic_mode.speed = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
dynamic_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
dynamic_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
dynamic_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(dynamic_mode);
// Default to Static mode
active_mode = 2;
SetupZones();
}
RGBController_EdifierHaloPixelbarAmbient::~RGBController_EdifierHaloPixelbarAmbient()
{
}
void RGBController_EdifierHaloPixelbarAmbient::SetupZones()
{
zone ambient_zone;
ambient_zone.name = "Light Effects";
ambient_zone.type = ZONE_TYPE_SINGLE;
ambient_zone.leds_min = 1;
ambient_zone.leds_max = 1;
ambient_zone.leds_count = 1;
zones.push_back(ambient_zone);
led ambient_led;
ambient_led.name = "Light Effects";
leds.push_back(ambient_led);
SetupColors();
}
void RGBController_EdifierHaloPixelbarAmbient::DeviceUpdateLEDs()
{
if(!first_leds_update_ignored)
{
first_leds_update_ignored = true;
return;
}
int active_idx = GetActiveMode();
RGBColor color = colors[0];
unsigned char r = RGBGetRValue(color);
unsigned char g = RGBGetGValue(color);
unsigned char b = RGBGetBValue(color);
unsigned char effect = modes[active_idx].value;
unsigned char brightness = modes[active_idx].brightness;
unsigned char speed = modes[active_idx].speed;
controller->SetAmbientLightPower(true);
controller->SetAmbientLight(effect, r, g, b, brightness, speed);
}
void RGBController_EdifierHaloPixelbarAmbient::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_EdifierHaloPixelbarAmbient::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_EdifierHaloPixelbarAmbient::DeviceUpdateMode()
{
if(!first_mode_update_ignored)
{
first_mode_update_ignored = true;
return;
}
DeviceUpdateLEDs();
}
@@ -1,35 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_EdifierHaloPixelbarAmbient.h |
| |
| RGBController for Edifier Halo PixelBar Ambient Light |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <memory>
#include "RGBController.h"
#include "EdifierHaloController.h"
class RGBController_EdifierHaloPixelbarAmbient : public RGBController
{
public:
RGBController_EdifierHaloPixelbarAmbient(EdifierHaloController* controller_ptr);
~RGBController_EdifierHaloPixelbarAmbient();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
private:
EdifierHaloController* controller;
bool first_leds_update_ignored;
bool first_mode_update_ignored;
};
@@ -1,173 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_EdifierHaloPixelbarScreen.cpp |
| |
| RGBController for Edifier Halo PixelBar Screen Color |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_EdifierHaloPixelbarScreen.h"
/**------------------------------------------------------------------*\
@name Edifier Halo Pixelbar - Screen
@category Speaker
@type USB
@save :robot:
@direct :x:
@effects :white_check_mark:
@detectors DetectEdifierHaloControllers
@comment
\*-------------------------------------------------------------------*/
struct CategoryDefinition
{
std::string name;
unsigned char category_id;
};
RGBController_EdifierHaloPixelbarScreen::RGBController_EdifierHaloPixelbarScreen(EdifierHaloController* controller_ptr)
{
controller = controller_ptr;
first_leds_update_ignored = false;
first_mode_update_ignored = false;
name = "Edifier Halo Pixelbar - Pixel Display";
vendor = "Edifier";
type = DEVICE_TYPE_SPEAKER;
description = "Edifier Halo PixelBar Pixel Display Color";
serial = controller->GetSerial();
location = controller->GetLocation();
// Clock 1 to Clock 11 (11 built-in clock styles)
for(unsigned char i = 0; i < 11; ++i)
{
mode m;
m.name = "Clock " + std::to_string(i + 1);
m.value = (0 << 8) | i;
m.flags = MODE_FLAG_HAS_PER_LED_COLOR;
m.color_mode = MODE_COLORS_PER_LED;
modes.push_back(m);
}
// Default built-in layouts for other categories
std::vector<CategoryDefinition> categories =
{
{ "Gaming", 1 },
{ "Work", 2 },
{ "Study", 3 },
{ "Cats", 4 },
{ "Dogs", 5 },
{ "Buzzwords", 6 },
{ "Cyberpunk", 7 },
{ "Spectrum", 8 }
};
for(const CategoryDefinition& cat : categories)
{
mode m;
m.name = cat.name;
m.value = (cat.category_id << 8) | 0;
m.flags = MODE_FLAG_HAS_PER_LED_COLOR;
m.color_mode = MODE_COLORS_PER_LED;
modes.push_back(m);
}
// Default to Clock 1 mode
active_mode = 0;
SetupZones();
}
RGBController_EdifierHaloPixelbarScreen::~RGBController_EdifierHaloPixelbarScreen()
{
delete controller;
}
void RGBController_EdifierHaloPixelbarScreen::SetupZones()
{
zone screen_zone;
screen_zone.name = "Screen Color";
screen_zone.type = ZONE_TYPE_SINGLE;
screen_zone.leds_min = 1;
screen_zone.leds_max = 1;
screen_zone.leds_count = 1;
zones.push_back(screen_zone);
led screen_led;
screen_led.name = "Screen Text";
leds.push_back(screen_led);
SetupColors();
}
void RGBController_EdifierHaloPixelbarScreen::DeviceUpdateLEDs()
{
if(!first_leds_update_ignored)
{
first_leds_update_ignored = true;
return;
}
unsigned char r = 255;
unsigned char g = 255;
unsigned char b = 255;
if(!colors.empty())
{
RGBColor color = colors[0];
r = RGBGetRValue(color);
g = RGBGetGValue(color);
b = RGBGetBValue(color);
}
// For all other modes, color updates are sent using SetPixelScreenColor
controller->SetPixelScreenColor(r, g, b);
}
void RGBController_EdifierHaloPixelbarScreen::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_EdifierHaloPixelbarScreen::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_EdifierHaloPixelbarScreen::DeviceUpdateMode()
{
if(!first_mode_update_ignored)
{
first_mode_update_ignored = true;
return;
}
int active_idx = GetActiveMode();
unsigned char r = 255;
unsigned char g = 255;
unsigned char b = 255;
if(!colors.empty())
{
RGBColor color = colors[0];
r = RGBGetRValue(color);
g = RGBGetGValue(color);
b = RGBGetBValue(color);
}
unsigned int mode_val = modes[active_idx].value;
unsigned char category = (mode_val >> 8) & 0xFF;
unsigned char index = mode_val & 0xFF;
if(category == 0) // Clock category (11 built-in styles)
{
controller->SetScreenScene(0, index, r, g, b);
}
else // Other categories (Gaming, Working, etc.) use the default built-in layouts
{
controller->SetScreenUIModel(category, r, g, b);
}
}
@@ -1,35 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_EdifierHaloPixelbarScreen.h |
| |
| RGBController for Edifier Halo PixelBar Screen Color |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <memory>
#include "RGBController.h"
#include "EdifierHaloController.h"
class RGBController_EdifierHaloPixelbarScreen : public RGBController
{
public:
RGBController_EdifierHaloPixelbarScreen(EdifierHaloController* controller_ptr);
~RGBController_EdifierHaloPixelbarScreen();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
private:
EdifierHaloController* controller;
bool first_leds_update_ignored;
bool first_mode_update_ignored;
};
@@ -0,0 +1,133 @@
/*---------------------------------------------------------*\
| EpomakerController.cpp |
| |
| Driver for Epomaker keyboard |
| |
| Alvaro Munoz (alvaromunoz) 05 Jun 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <math.h>
#include "EpomakerController.h"
#include "LogManager.h"
#include "StringUtils.h"
EpomakerController::EpomakerController(hid_device* dev_handle, char *_path)
{
dev = dev_handle;
location = _path;
/*---------------------------------------------------------*\
| Get device name from HID manufacturer and product strings |
\*---------------------------------------------------------*/
wchar_t name_string[HID_MAX_STR];
hid_get_manufacturer_string(dev, name_string, HID_MAX_STR);
device_name = StringUtils::wstring_to_string(name_string);
hid_get_product_string(dev, name_string, HID_MAX_STR);
device_name.append(" ").append(StringUtils::wstring_to_string(name_string));
current_mode = EPOMAKER_MODE_ALWAYS_ON;
current_speed = EPOMAKER_SPEED_DEFAULT;
current_brightness = EPOMAKER_BRIGHTNESS_DEFAULT;
current_dazzle = EPOMAKER_OPTION_DAZZLE_OFF;
current_option = EPOMAKER_OPTION_DEFAULT;
}
EpomakerController::~EpomakerController()
{
hid_close(dev);
}
std::string EpomakerController::GetDeviceName()
{
return(device_name);
}
std::string EpomakerController::GetSerial()
{
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));
}
std::string EpomakerController::GetLocation()
{
return("HID: " + location);
}
void EpomakerController::SetMode(unsigned char mode, unsigned char speed, unsigned char brightness)
{
current_mode = mode;
current_speed = speed;
current_brightness = brightness;
SendUpdate();
}
void EpomakerController::SetColor(unsigned char red, unsigned char green, unsigned char blue)
{
current_red = red;
current_green = green;
current_blue = blue;
SendUpdate();
}
void EpomakerController::SetDazzle(bool is_dazzle)
{
if(is_dazzle)
{
current_dazzle = EPOMAKER_OPTION_DAZZLE_ON;
}
else
{
current_dazzle = EPOMAKER_OPTION_DAZZLE_OFF;
}
}
void EpomakerController::SetOption(unsigned char option)
{
current_option = option;
}
void EpomakerController::SendUpdate()
{
unsigned char buffer[EPOMAKER_PACKET_LENGTH + 1] = { 0x00 };
buffer[EPOMAKER_BYTE_COMMAND] = EPOMAKER_COMMAND_RGB;
buffer[EPOMAKER_BYTE_MODE] = current_mode;
buffer[EPOMAKER_BYTE_SPEED] = current_speed;
buffer[EPOMAKER_BYTE_BRIGHTNESS] = current_brightness;
buffer[EPOMAKER_BYTE_FLAGS] = current_option | current_dazzle;
buffer[EPOMAKER_BYTE_RED] = current_red;
buffer[EPOMAKER_BYTE_GREEN] = current_green;
buffer[EPOMAKER_BYTE_BLUE] = current_blue;
int sum_bits = 0;
for(int i = EPOMAKER_BYTE_COMMAND; i <= EPOMAKER_BYTE_BLUE; i++)
{
sum_bits += buffer[i];
}
int next_pow2 = (int)(pow(2, ceil(log2((double)(sum_bits)))));
int filler = next_pow2 - sum_bits - 1;
buffer[EPOMAKER_BYTE_FILLER] = filler;
int send_buffer_result = hid_send_feature_report(dev, buffer, (sizeof(buffer) / sizeof(buffer[0])));
if(send_buffer_result<0)
{
LOG_ERROR("[EPOMAKER]: Send Buffer Error. HIDAPI Error: %ls", hid_error(dev));
}
}
@@ -13,7 +13,6 @@
#include <string>
#include <hidapi.h>
#include "../RoyuanKeyboardController/RoyuanKeyboardController.h"
#define EPOMAKER_PACKET_LENGTH 0x40
#define EPOMAKER_COMMAND_RGB 0x07
@@ -91,9 +90,36 @@ enum
EPOMAKER_OPTION_PEAK_TURN_CLOCKWISE = 0x10,
};
class EpomakerController : public RoyuanKeyboardController
class EpomakerController
{
public:
EpomakerController(hid_device* dev_handle, char* path);
EpomakerController(hid_device* dev_handle, char *_path);
~EpomakerController();
std::string GetDeviceName();
std::string GetSerial();
std::string GetLocation();
void SetDazzle(bool is_dazzle);
void SetOption(unsigned char option);
void SetMode(unsigned char mode, unsigned char speed, unsigned char brightness);
void SetColor(unsigned char red, unsigned char green, unsigned char blue);
private:
std::string device_name;
std::string location;
hid_device* dev;
unsigned char current_mode;
unsigned char current_speed;
unsigned char current_brightness;
unsigned char current_dazzle;
unsigned char current_option;
unsigned char current_red;
unsigned char current_green;
unsigned char current_blue;
void SendUpdate();
};
@@ -0,0 +1,50 @@
/*---------------------------------------------------------*\
| EpomakerControllerDetect.cpp |
| |
| Detector for Epomaker keyboard |
| |
| Alvaro Munoz (alvaromunoz) 05 Jun 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "EpomakerController.h"
#include "RGBController_EpomakerController.h"
#define EPOMAKER_VID 0x3151
#define EPOMAKER_TH80_Pro_USB_PID 0x4010
#define ATTACKSHARK_K86_USB_PID 0x4015
#define EPOMAKER_TH80_Pro_Dongle_PID 0x4011 /* Attack shark's Dongle is the same. */
#define EPOMAKER_TH80_Pro_BT_PID 0x4013
#define ATTACKSHARK_K86_BT_PID 0x401
DetectedControllers DetectEpomakerControllers(hid_device_info* info, const std::string&)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
EpomakerController* controller = new EpomakerController(dev, info->path);
RGBController_EpomakerController* rgb_controller = new RGBController_EpomakerController(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_I("Epomaker TH80 Pro (USB Cable)", DetectEpomakerControllers, EPOMAKER_VID, EPOMAKER_TH80_Pro_USB_PID, 2);
REGISTER_HID_DETECTOR_I("Epomaker TH80 Pro (USB Dongle)", DetectEpomakerControllers, EPOMAKER_VID, EPOMAKER_TH80_Pro_Dongle_PID, 2);
REGISTER_HID_DETECTOR_I("Attack Shark K86 (USB Cable)", DetectEpomakerControllers, EPOMAKER_VID, ATTACKSHARK_K86_USB_PID, 2);
/*---------------------------------------------------------*\
| Bluetooth Not implemented |
\*---------------------------------------------------------*/
//REGISTER_HID_DETECTOR("Epomaker TH80 Pro (Bluetooth)", DetectEpomakerControllers, EPOMAKER_VID, EPOMAKER_TH80_Pro_BT_PID);
//REGISTER_HID_DETECTOR("Attack Shark K86 (Bluetooth)", DetectEpomakerControllers, EPOMAKER_VID, ATTACKSHARK_K86_BT_PID);
@@ -19,7 +19,7 @@
@save :robot:
@direct :x:
@effects :white_check_mark:
@detectors DetectRoyuanLegacyKeyboardControllers
@detectors DetectEpomakerControllers
@comment
\*-------------------------------------------------------------------*/
@@ -44,9 +44,8 @@ bool TestForGigabyteRGBFusion2BlackwellGPUController(i2c_smbus_interface* bus, u
//GeForce RTX 5070 Ti Gaming OC 16G 0x01 0x01 0x01 0x00
//GeForce RTX 5070 Gaming OC 12G 0x01 0x01 0x01 0x00
//GeForce RTX 5080 AORUS MASTER 16G 0x01 0x01 0x01 0x10
//Radeon RX 9070 XT GAMING OC 16G 0x01 0x02 0x01 0x00
if(res < 0 || data_readpkt[0] != 0x01 || (data_readpkt[1] != 0x01 && data_readpkt[1] != 0x02) || data_readpkt[2] != 0x01)
if(res < 0 || data_readpkt[0] != 0x01 || data_readpkt[1] != 0x01 || data_readpkt[2] != 0x01)
{
// Assemble C-string with respons for debugging
std::string text = "";
@@ -58,7 +57,7 @@ bool TestForGigabyteRGBFusion2BlackwellGPUController(i2c_smbus_interface* bus, u
text.append(str);
}
LOG_DEBUG("[%s] at address 0x%02X invalid. Expected 0x01 0x01/0x02 0x01 [0x*] but received:%s", GIGABYTEGPU_CONTROLLER_NAME3, address, text.c_str());
LOG_DEBUG("[%s] at address 0x%02X invalid. Expected 0x01 0x01 0x01 [0x*] but received:%s", GIGABYTEGPU_CONTROLLER_NAME3, address, text.c_str());
pass = false;
}
@@ -596,8 +596,8 @@ static const gb_fusion2_zone common_team_up_logo_11_zone =
static const gb_fusion2_zone common_wifi_ant_9_zone =
{
LED9,
2,
2,
1,
1,
"WIFI Antenna"
};
@@ -3930,23 +3930,6 @@ static const gb_fusion2_device b550_vision_dp_device =
"B550 VISION D-P",
};
static const gb_fusion2_device b550_ud_ac_device =
{
&it5702_24_device,
0x0140005F,
0,
"B550 UD AC",
};
static const gb_fusion2_device b550_ud_ac_y1_device =
{
&it5702_24_device,
0x0140005F,
0,
"B550 UD AC-Y1",
};
static const gb_fusion2_device b550m_aor_elite_device =
{
&it5702_24_device,
@@ -4559,14 +4542,6 @@ static const gb_fusion2_device z490_aor_ultra_g2_device =
"Z490 AORUS ULTRA G2",
};
static const gb_fusion2_device z490_vision_d_device =
{
&it5702_30_device,
0x0180005F,
0,
"Z490 VISION D",
};
static const gb_fusion2_device b760m_aor_pro_device =
{
&it5702_30_device,
@@ -4919,13 +4894,48 @@ static const gb_fusion2_device b760m_aor_elite_x_ax_device =
| |
| Zone "D_LED1" : Linear |
| Zone "D_LED2" : Linear |
| Zone "IO Cover" : Single |
| Zone "LED_C1" : Single |
| Zone "Chipset Accent" : Single |
| Zone "PCI-E Accent" : Single |
| Zone "LED_C2" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_36_device =
{
&common_d_led1_zone,
&common_d_led2_zone,
&common_io_cov_btm_1_zone,
&common_io_cov_btm_mid_2_zone,
&common_chip_io_cov_tm_3_zone,
&common_io_cov_top_4_zone,
&common_led_c2_5_zone,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
};
static const gb_fusion2_device z490_vision_d_device =
{
&it5702_36_device,
0x0180005F,
0,
"Z490 VISION D",
};
/*-------------------------------------------------------------*\
| Layout 37 048D:5702 |
| |
| Zone "D_LED1" : Linear |
| Zone "D_LED2" : Linear |
| Zone "IO Cover (Top)" : Single |
| Zone "LED_C1" : Single |
| Zone "LED_CPU" : Single |
| Zone "IO Cover (Bottom)" : Single |
| Zone "LED_C2" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_36_device =
static gb_fusion2_layout it5702_37_device =
{
&common_d_led1_zone,
&common_d_led2_zone,
@@ -4943,7 +4953,7 @@ static gb_fusion2_layout it5702_36_device =
static const gb_fusion2_device b650_aor_pro_ax_device =
{
&it5702_36_device,
&it5702_37_device,
0x09A001DF,
0,
"B650 AORUS PRO AX",
@@ -4951,7 +4961,7 @@ static const gb_fusion2_device b650_aor_pro_ax_device =
static const gb_fusion2_device b650e_aor_mstr_device =
{
&it5702_36_device,
&it5702_37_device,
0x09A001DF,
0,
"B650E AORUS MASTER",
@@ -4959,14 +4969,14 @@ static const gb_fusion2_device b650e_aor_mstr_device =
static const gb_fusion2_device x670e_aor_mstr_device =
{
&it5702_36_device,
&it5702_37_device,
0x024001DF,
0,
"X670E AORUS MASTER",
};
/*-------------------------------------------------------------*\
| Layout 37 048D:5702 |
| Layout 38 048D:5702 |
| |
| Zone "D_LED1" : Linear |
| Zone "D_LED2" : Linear |
@@ -4976,7 +4986,7 @@ static const gb_fusion2_device x670e_aor_mstr_device =
| Zone "Board Accent (Right Side, Top Middle)" : Single |
| Zone "LED_C2" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_37_device =
static gb_fusion2_layout it5702_38_device =
{
&common_d_led1_zone,
&common_d_led2_zone,
@@ -4994,7 +5004,7 @@ static gb_fusion2_layout it5702_37_device =
static const gb_fusion2_device b660_aor_mstr_device =
{
&it5702_37_device,
&it5702_38_device,
0x094001DF,
0,
"B660 AORUS MASTER",
@@ -5002,7 +5012,7 @@ static const gb_fusion2_device b660_aor_mstr_device =
static const gb_fusion2_device b660_aor_mstr_ddr4_device =
{
&it5702_37_device,
&it5702_38_device,
0x094001DF,
0,
"B660 AORUS MASTER DDR4",
@@ -5010,7 +5020,7 @@ static const gb_fusion2_device b660_aor_mstr_ddr4_device =
static const gb_fusion2_device b760_aor_mstr_ddr4_device =
{
&it5702_37_device,
&it5702_38_device,
0x094001DF,
0,
"B760 AORUS MASTER DDR4",
@@ -5018,7 +5028,7 @@ static const gb_fusion2_device b760_aor_mstr_ddr4_device =
static const gb_fusion2_device z690_aor_elite_device =
{
&it5702_37_device,
&it5702_38_device,
0x016001DF,
0,
"Z690 AORUS ELITE",
@@ -5026,7 +5036,7 @@ static const gb_fusion2_device z690_aor_elite_device =
static const gb_fusion2_device z690_aor_elite_ax_2_device =
{
&it5702_37_device,
&it5702_38_device,
0x016001DF,
0,
"Z690 AORUS ELITE AX",
@@ -5034,7 +5044,7 @@ static const gb_fusion2_device z690_aor_elite_ax_2_device =
static const gb_fusion2_device z690_aor_elite_ax_ddr4_device =
{
&it5702_37_device,
&it5702_38_device,
0x016001DF,
0,
"Z690 AORUS ELITE AX DDR4",
@@ -5042,7 +5052,7 @@ static const gb_fusion2_device z690_aor_elite_ax_ddr4_device =
static const gb_fusion2_device z690_aor_elite_ax_ddr4_v2_device =
{
&it5702_37_device,
&it5702_38_device,
0x016001DF,
0,
"Z690 AORUS ELITE AX DDR4 V2",
@@ -5050,7 +5060,7 @@ static const gb_fusion2_device z690_aor_elite_ax_ddr4_v2_device =
static const gb_fusion2_device z690_aor_elite_ddr4_device =
{
&it5702_37_device,
&it5702_38_device,
0x016001DF,
0,
"Z690 AORUS ELITE DDR4",
@@ -5067,7 +5077,7 @@ static const gb_fusion2_device z690_aor_elite_ddr4_device =
| Zone "PCI-E Accent" : Single |
| Zone "LED_C2" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_38_device =
static gb_fusion2_layout it5702_39_device =
{
&common_d_led1_zone,
&common_d_led2_zone,
@@ -5085,7 +5095,7 @@ static gb_fusion2_layout it5702_38_device =
static const gb_fusion2_device b550_aor_elite_device =
{
&it5702_38_device,
&it5702_39_device,
0x0170005F,
0,
"B550 AORUS ELITE",
@@ -5093,7 +5103,7 @@ static const gb_fusion2_device b550_aor_elite_device =
static const gb_fusion2_device b550_aor_elite_ax_device =
{
&it5702_38_device,
&it5702_39_device,
0x0170005F,
0,
"B550 AORUS ELITE AX",
@@ -5101,7 +5111,7 @@ static const gb_fusion2_device b550_aor_elite_ax_device =
static const gb_fusion2_device b550_aor_elite_ax_v2_device =
{
&it5702_38_device,
&it5702_39_device,
0x0170005F,
0,
"B550 AORUS ELITE AX V2",
@@ -5109,7 +5119,7 @@ static const gb_fusion2_device b550_aor_elite_ax_v2_device =
static const gb_fusion2_device b550_aor_elite_ax_v3_device =
{
&it5702_38_device,
&it5702_39_device,
0x0170005F,
0,
"B550 AORUS ELITE AX V3",
@@ -5117,7 +5127,7 @@ static const gb_fusion2_device b550_aor_elite_ax_v3_device =
static const gb_fusion2_device b550_aor_elite_v2_device =
{
&it5702_38_device,
&it5702_39_device,
0x0170005F,
0,
"B550 AORUS ELITE V2",
@@ -5125,7 +5135,7 @@ static const gb_fusion2_device b550_aor_elite_v2_device =
static const gb_fusion2_device b550_aor_pro_device =
{
&it5702_38_device,
&it5702_39_device,
0x0170005F,
0,
"B550 AORUS PRO",
@@ -5133,7 +5143,7 @@ static const gb_fusion2_device b550_aor_pro_device =
static const gb_fusion2_device b550_aor_pro_ac_device =
{
&it5702_38_device,
&it5702_39_device,
0x0170005F,
0,
"B550 AORUS PRO AC",
@@ -5141,7 +5151,7 @@ static const gb_fusion2_device b550_aor_pro_ac_device =
static const gb_fusion2_device b550_aor_pro_ax_device =
{
&it5702_38_device,
&it5702_39_device,
0x0170005F,
0,
"B550 AORUS PRO AX",
@@ -5149,7 +5159,7 @@ static const gb_fusion2_device b550_aor_pro_ax_device =
static const gb_fusion2_device b550_aor_pro_v2_device =
{
&it5702_38_device,
&it5702_39_device,
0x0170005F,
0,
"B550 AORUS PRO V2",
@@ -5157,7 +5167,7 @@ static const gb_fusion2_device b550_aor_pro_v2_device =
static const gb_fusion2_device x570s_aor_pro_ax_device =
{
&it5702_38_device,
&it5702_39_device,
0x0150015F,
0,
"X570S AORUS PRO AX",
@@ -5168,7 +5178,7 @@ static const gb_fusion2_device x570s_aor_pro_ax_device =
| |
| Zone "IO Cover" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_39_device =
static gb_fusion2_layout it5702_40_device =
{
&common_io_cov_1_zone,
nullptr,
@@ -5186,7 +5196,7 @@ static gb_fusion2_layout it5702_39_device =
static const gb_fusion2_device z790_aor_mstr_x_2_device =
{
&it5702_39_device,
&it5702_40_device,
0x123001DF,
1,
"Z790 AORUS MASTER X",
@@ -5194,7 +5204,7 @@ static const gb_fusion2_device z790_aor_mstr_x_2_device =
static const gb_fusion2_device z790_aor_mstr_2_device =
{
&it5702_39_device,
&it5702_40_device,
0x015001DF,
1,
"Z790 AORUS MASTER",
@@ -5202,7 +5212,7 @@ static const gb_fusion2_device z790_aor_mstr_2_device =
static const gb_fusion2_device x670_aor_xtrm_2_device =
{
&it5702_39_device,
&it5702_40_device,
0x023001DF,
1,
"X670E AORUS XTREME",
@@ -5213,7 +5223,7 @@ static const gb_fusion2_device x670_aor_xtrm_2_device =
| |
| Zone "Chipset Accent" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_40_device =
static gb_fusion2_layout it5702_41_device =
{
&common_chip_acc_3_zone,
nullptr,
@@ -5231,7 +5241,7 @@ static gb_fusion2_layout it5702_40_device =
static const gb_fusion2_device z790_aor_xtrm_2_device =
{
&it5702_40_device,
&it5702_41_device,
0x026001DF,
1,
"Z790 AORUS XTREME",
@@ -5239,7 +5249,7 @@ static const gb_fusion2_device z790_aor_xtrm_2_device =
static const gb_fusion2_device z790_aor_xtrm_x_2_device =
{
&it5702_40_device,
&it5702_41_device,
0x124001DF,
1,
"Z790 AORUS XTREME X",
@@ -5251,7 +5261,7 @@ static const gb_fusion2_device z790_aor_xtrm_x_2_device =
| Zone "IO Cover" : Single |
| Zone "LED_CPU" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_41_device =
static gb_fusion2_layout it5702_42_device =
{
&common_chip_acc_3_zone,
&common_led_cpu_8_zone,
@@ -5269,7 +5279,7 @@ static gb_fusion2_layout it5702_41_device =
static const gb_fusion2_device z690_aor_xtrm_waterforce_2_device =
{
&it5702_41_device,
&it5702_42_device,
0x029001DF,
1,
"Z690 AORUS XTREME WATERFORCE",
@@ -5282,7 +5292,7 @@ static const gb_fusion2_device z690_aor_xtrm_waterforce_2_device =
| Zone "Chipset Accent" : Single |
| Zone "LED_CPU" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_42_device =
static gb_fusion2_layout it5702_43_device =
{
&common_chip_acc_3_zone,
&common_chip_acc_3_zone,
@@ -5300,7 +5310,7 @@ static gb_fusion2_layout it5702_42_device =
static const gb_fusion2_device z490_aor_xtrm_2_device =
{
&it5702_42_device,
&it5702_43_device,
0x029001DF,
1,
"Z490 AORUS XTREME",
@@ -5308,7 +5318,7 @@ static const gb_fusion2_device z490_aor_xtrm_2_device =
static const gb_fusion2_device z490_aor_xtrm_waterforce_2_device =
{
&it5702_42_device,
&it5702_43_device,
0x029001DF,
1,
"Z490 AORUS XTREME WATERFORCE",
@@ -5316,7 +5326,7 @@ static const gb_fusion2_device z490_aor_xtrm_waterforce_2_device =
static const gb_fusion2_device z590_aor_xtrm_waterforce_2_device =
{
&it5702_42_device,
&it5702_43_device,
0x029001DF,
1,
"Z590 AORUS XTREME WATERFORCE",
@@ -5330,7 +5340,7 @@ static const gb_fusion2_device z590_aor_xtrm_waterforce_2_device =
| Zone "RAM Cover" : Single |
| Zone "LED_CPU" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_43_device =
static gb_fusion2_layout it5702_44_device =
{
&common_chip_acc_3_zone,
&common_chip_acc_3_zone,
@@ -5348,7 +5358,7 @@ static gb_fusion2_layout it5702_43_device =
static const gb_fusion2_device z690_aor_xtrm_2_device =
{
&it5702_43_device,
&it5702_44_device,
0x028001DF,
1,
"Z690 AORUS XTREME",
@@ -5365,7 +5375,7 @@ static const gb_fusion2_device z690_aor_xtrm_2_device =
| Zone "IO Cover (Bottom)" : Single |
| Zone "LED_C2" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_44_device =
static gb_fusion2_layout it5702_45_device =
{
&common_d_led1_zone,
&common_d_led2_zone,
@@ -5383,7 +5393,7 @@ static gb_fusion2_layout it5702_44_device =
static const gb_fusion2_device z690_aor_mstr_device =
{
&it5702_44_device,
&it5702_45_device,
0x017001DF,
0,
"Z690 AORUS MASTER",
@@ -5399,7 +5409,7 @@ static const gb_fusion2_device z690_aor_mstr_device =
| Zone "Board Accent (Right Side)" : Single |
| Zone "LED_C2" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_45_device =
static gb_fusion2_layout it5702_46_device =
{
&common_d_led1_zone,
&common_d_led2_zone,
@@ -5417,7 +5427,7 @@ static gb_fusion2_layout it5702_45_device =
static const gb_fusion2_device x570s_aor_elite_device =
{
&it5702_45_device,
&it5702_46_device,
0x0120015F,
0,
"X570S AORUS ELITE",
@@ -5425,7 +5435,7 @@ static const gb_fusion2_device x570s_aor_elite_device =
static const gb_fusion2_device x570s_aor_elite_ax_device =
{
&it5702_45_device,
&it5702_46_device,
0x0120015F,
0,
"X570S AORUS ELITE AX",
@@ -5433,7 +5443,7 @@ static const gb_fusion2_device x570s_aor_elite_ax_device =
static const gb_fusion2_device x570s_gmg_x_device =
{
&it5702_45_device,
&it5702_46_device,
0x0120015F,
0,
"X570S GAMING X",
@@ -5448,7 +5458,7 @@ static const gb_fusion2_device x570s_gmg_x_device =
| Zone "Aorus Logo" : Single |
| Zone "LED_C2" : Single |
\*-------------------------------------------------------------*/
static gb_fusion2_layout it5702_46_device =
static gb_fusion2_layout it5702_47_device =
{
&common_d_led1_zone,
&common_d_led2_zone,
@@ -5465,7 +5475,7 @@ static gb_fusion2_layout it5702_46_device =
static const gb_fusion2_device a520m_aor_elite_device =
{
&it5702_46_device,
&it5702_47_device,
0x0050005F,
0,
"A520M AORUS ELITE",
@@ -6977,6 +6987,7 @@ static const gb_fusion2_device z890_aor_mstr_ai_top_5711_device =
| Zone "ARGB_V2_1" : Linear |
| Zone "ARGB_V2_2" : Linear |
| Zone "ARGB_V2_3" : Linear |
| Zone "ARGB_V2_4" : Linear |
| Zone "LED_C" : Single |
| Zone "WIFI Antenna" : Single |
| Zone "IO Cover" : Single |
@@ -6987,6 +6998,7 @@ static gb_fusion2_layout it5711_14_device =
&common_argb_v2_1_zone,
&common_argb_v2_2_zone,
&common_argb_v2_3_zone,
&common_argb_v2_4_zone,
&common_led_c_5_zone,
&common_wifi_ant_9_zone,
&common_io_cov_10_zone,
@@ -6995,7 +7007,6 @@ static gb_fusion2_layout it5711_14_device =
nullptr,
nullptr,
nullptr,
nullptr,
};
static const gb_fusion2_device x870e_aor_xtrm_ai_top_5711_device =
@@ -7382,8 +7393,6 @@ const gb_fusion2_device* gb_fusion2_device_list_data[] =
&b550_gmg_x_v2_device,
&b550_vision_d_device,
&b550_vision_dp_device,
&b550_ud_ac_device,
&b550_ud_ac_y1_device,
&b550i_aor_pro_ax_device,
&b550m_aor_elite_device,
&b550m_aor_elite_ax_device,
@@ -575,7 +575,7 @@ void RGBController_RGBFusion2USB::SetupZones()
continue;
}
bool fixed_zone = (zone_at_idx->leds_min == 1 && zone_at_idx->leds_max == 1);
bool fixed_zone = (zone_at_idx->leds_min == zone_at_idx->leds_max);
const Gen2StripInfo* gen2_info = nullptr;
@@ -208,8 +208,7 @@ void NanoleafController::UpdateLEDs(std::vector<RGBColor>& colors)
free(message);
}
else if((model == NANOLEAF_CANVAS_MODEL)
|| (model == NANOLEAF_SHAPES_MODEL)
|| (model == NANOLEAF_LINES_MODEL))
|| (model == NANOLEAF_SHAPES_MODEL))
{
/*---------------------------------------------------------*\
| Protocol V2 - https://forum.nanoleaf.me/docs |
@@ -276,8 +275,7 @@ void NanoleafController::StartExternalControl()
}
}
else if((model == NANOLEAF_CANVAS_MODEL)
|| (model == NANOLEAF_SHAPES_MODEL)
|| (model == NANOLEAF_LINES_MODEL))
|| (model == NANOLEAF_SHAPES_MODEL))
{
/*---------------------------------------------------------*\
| Protocol v2 does not return anything, use device IP and |
@@ -18,7 +18,6 @@
#define NANOLEAF_LIGHT_PANELS_MODEL "NL22"
#define NANOLEAF_CANVAS_MODEL "NL29"
#define NANOLEAF_SHAPES_MODEL "NL42"
#define NANOLEAF_LINES_MODEL "NL59"
class NanoleafController
{
@@ -1,184 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AkkoKeyboard.cpp |
| |
| RGBController for Akko Multi-modes keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_AkkoKeyboard.h"
/**------------------------------------------------------------------*\
@name Akko 3068B Plus
@category Keyboard
@type USB
@save :white_check_mark:
@direct :x:
@effects :white_check_mark:
@detectors DetectRoyuanAkkoKeyboardControllers
@comment
Global-color hardware lighting support for Akko/RongYuan keyboards
using USB ID 3151:4003 in wired mode.
\*-------------------------------------------------------------------*/
RGBController_AkkoKeyboard::RGBController_AkkoKeyboard(RoyuanKeyboardController* controller_ptr)
{
controller = controller_ptr;
name = "Akko 3068B Plus";
vendor = "Akko";
type = DEVICE_TYPE_KEYBOARD;
description = "Akko 3068B Plus lighting controller";
serial = controller->GetSerial();
location = controller->GetLocation();
mode off;
off.name = "Off";
off.value = ROYUAN_AKKO_EFFECT_OFF;
off.flags = MODE_FLAG_AUTOMATIC_SAVE;
off.color_mode = MODE_COLORS_NONE;
modes.push_back(off);
mode static_mode;
static_mode.name = "Static";
static_mode.value = ROYUAN_AKKO_EFFECT_STATIC;
static_mode.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR;
static_mode.color_mode = MODE_COLORS_PER_LED;
static_mode.brightness_min = ROYUAN_AKKO_BRIGHTNESS_MIN;
static_mode.brightness_max = ROYUAN_AKKO_BRIGHTNESS_MAX;
static_mode.brightness = ROYUAN_AKKO_BRIGHTNESS_DEFAULT;
modes.push_back(static_mode);
mode breathing;
breathing.name = "Breathing";
breathing.value = ROYUAN_AKKO_EFFECT_BREATHING;
breathing.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
breathing.color_mode = MODE_COLORS_PER_LED;
breathing.brightness_min = ROYUAN_AKKO_BRIGHTNESS_MIN;
breathing.brightness_max = ROYUAN_AKKO_BRIGHTNESS_MAX;
breathing.brightness = ROYUAN_AKKO_BRIGHTNESS_DEFAULT;
breathing.speed_min = ROYUAN_AKKO_SPEED_MIN;
breathing.speed_max = ROYUAN_AKKO_SPEED_MAX;
breathing.speed = ROYUAN_AKKO_SPEED_DEFAULT;
modes.push_back(breathing);
mode wave;
wave.name = "Wave";
wave.value = ROYUAN_AKKO_EFFECT_WAVE;
wave.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
wave.color_mode = MODE_COLORS_PER_LED;
wave.brightness_min = ROYUAN_AKKO_BRIGHTNESS_MIN;
wave.brightness_max = ROYUAN_AKKO_BRIGHTNESS_MAX;
wave.brightness = ROYUAN_AKKO_BRIGHTNESS_DEFAULT;
wave.speed_min = ROYUAN_AKKO_SPEED_MIN;
wave.speed_max = ROYUAN_AKKO_SPEED_MAX;
wave.speed = ROYUAN_AKKO_SPEED_DEFAULT;
modes.push_back(wave);
mode ripple;
ripple.name = "Ripple";
ripple.value = ROYUAN_AKKO_EFFECT_RIPPLE;
ripple.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
ripple.color_mode = MODE_COLORS_PER_LED;
ripple.brightness_min = ROYUAN_AKKO_BRIGHTNESS_MIN;
ripple.brightness_max = ROYUAN_AKKO_BRIGHTNESS_MAX;
ripple.brightness = ROYUAN_AKKO_BRIGHTNESS_DEFAULT;
ripple.speed_min = ROYUAN_AKKO_SPEED_MIN;
ripple.speed_max = ROYUAN_AKKO_SPEED_MAX;
ripple.speed = ROYUAN_AKKO_SPEED_DEFAULT;
modes.push_back(ripple);
mode reactive;
reactive.name = "Reactive";
reactive.value = ROYUAN_AKKO_EFFECT_REACTIVE;
reactive.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
reactive.color_mode = MODE_COLORS_PER_LED;
reactive.brightness_min = ROYUAN_AKKO_BRIGHTNESS_MIN;
reactive.brightness_max = ROYUAN_AKKO_BRIGHTNESS_MAX;
reactive.brightness = ROYUAN_AKKO_BRIGHTNESS_DEFAULT;
reactive.speed_min = ROYUAN_AKKO_SPEED_MIN;
reactive.speed_max = ROYUAN_AKKO_SPEED_MAX;
reactive.speed = ROYUAN_AKKO_SPEED_DEFAULT;
modes.push_back(reactive);
for(unsigned int mode_index = 0; mode_index < modes.size(); mode_index++)
{
if(modes[mode_index].value == controller->GetMode())
{
active_mode = mode_index;
break;
}
}
if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS)
{
modes[active_mode].brightness = controller->GetBrightness();
}
if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED)
{
modes[active_mode].speed = controller->GetSpeed();
}
SetupZones();
colors[0] = ToRGBColor(controller->GetRed(), controller->GetGreen(), controller->GetBlue());
}
RGBController_AkkoKeyboard::~RGBController_AkkoKeyboard()
{
delete controller;
}
void RGBController_AkkoKeyboard::SetupZones()
{
zone keyboard_zone;
keyboard_zone.name = "Keyboard";
keyboard_zone.type = ZONE_TYPE_SINGLE;
keyboard_zone.leds_min = 1;
keyboard_zone.leds_max = 1;
keyboard_zone.leds_count = 1;
keyboard_zone.matrix_map.Reset();
zones.push_back(keyboard_zone);
led keyboard_led;
keyboard_led.name = "Keyboard LEDs";
keyboard_led.value = 0x00;
leds.push_back(keyboard_led);
SetupColors();
}
void RGBController_AkkoKeyboard::ResizeZone(int /*zone*/, int /*new_size*/)
{
}
void RGBController_AkkoKeyboard::DeviceUpdateLEDs()
{
unsigned char red = RGBGetRValue(colors[0]);
unsigned char grn = RGBGetGValue(colors[0]);
unsigned char blu = RGBGetBValue(colors[0]);
controller->SetColor(red, grn, blu);
}
void RGBController_AkkoKeyboard::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_AkkoKeyboard::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_AkkoKeyboard::DeviceUpdateMode()
{
controller->SetMode(
modes[active_mode].value,
modes[active_mode].speed,
modes[active_mode].brightness
);
DeviceUpdateLEDs();
}
@@ -1,32 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AkkoKeyboard.h |
| |
| RGBController for Akko Multi-modes keyboards |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "RoyuanKeyboardController.h"
class RGBController_AkkoKeyboard : public RGBController
{
public:
RGBController_AkkoKeyboard(RoyuanKeyboardController* controller_ptr);
~RGBController_AkkoKeyboard();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
RoyuanKeyboardController* controller;
};
@@ -1,21 +0,0 @@
/*---------------------------------------------------------*\
| EpomakerController.cpp |
| |
| Compatibility wrapper for ROYUAN-based keyboards |
| |
| Alvaro Munoz (alvaromunoz) 05 Jun 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "EpomakerController.h"
EpomakerController::EpomakerController(hid_device* dev_handle, char* path)
: RoyuanKeyboardController(dev_handle, path, RoyuanKeyboardProfile::EpomakerLegacy())
{
}
EpomakerController::~EpomakerController()
{
}
@@ -1,308 +0,0 @@
/*---------------------------------------------------------*\
| RoyuanKeyboardController.cpp |
| |
| Driver for ROYUAN-based keyboard lighting controllers |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RoyuanKeyboardController.h"
#include "LogManager.h"
#include "StringUtils.h"
#include <algorithm>
#include <chrono>
#include <cstring>
#include <thread>
RoyuanKeyboardProfile RoyuanKeyboardProfile::EpomakerLegacy()
{
RoyuanKeyboardProfile profile;
profile.protocol = RoyuanKeyboardProtocol::Legacy;
profile.checksum_type = RoyuanChecksumType::NextPowerOfTwo;
profile.speed_max = 0x05;
profile.speed_default = 0x04;
profile.brightness_max = 0x04;
profile.brightness_default = 0x04;
profile.option_default = 0x00;
profile.dazzle_default = 0x07;
profile.reverse_speed = false;
profile.supports_readback = false;
profile.fallback_name = "ROYUAN Keyboard";
return(profile);
}
RoyuanKeyboardProfile RoyuanKeyboardProfile::AkkoBSeries()
{
RoyuanKeyboardProfile profile;
profile.protocol = RoyuanKeyboardProtocol::AkkoBSeries;
profile.checksum_type = RoyuanChecksumType::OnesComplement;
profile.speed_max = ROYUAN_AKKO_SPEED_MAX;
profile.speed_default = ROYUAN_AKKO_SPEED_DEFAULT;
profile.brightness_max = ROYUAN_AKKO_BRIGHTNESS_MAX;
profile.brightness_default = ROYUAN_AKKO_BRIGHTNESS_DEFAULT;
profile.option_default = 0x08;
profile.dazzle_default = 0x00;
profile.reverse_speed = true;
profile.supports_readback = true;
profile.fallback_name = "Akko 3068B Plus";
return(profile);
}
RoyuanKeyboardController::RoyuanKeyboardController(hid_device* dev_handle, char* path, const RoyuanKeyboardProfile& profile)
{
dev = dev_handle;
location = path;
this->profile = profile;
valid = false;
current_mode = 0x01;
current_speed = profile.speed_default;
current_brightness = profile.brightness_default;
current_dazzle = profile.dazzle_default;
current_option = profile.option_default;
current_red = 0x00;
current_green = 0x00;
current_blue = 0x00;
wchar_t name_string[ROYUAN_HID_MAX_STR] = { 0 };
if(hid_get_manufacturer_string(dev, name_string, ROYUAN_HID_MAX_STR) == 0)
{
device_name = StringUtils::wstring_to_string(name_string);
}
std::memset(name_string, 0, sizeof(name_string));
if(hid_get_product_string(dev, name_string, ROYUAN_HID_MAX_STR) == 0)
{
std::string product_name = StringUtils::wstring_to_string(name_string);
if(!product_name.empty())
{
if(!device_name.empty())
{
device_name.append(" ");
}
device_name.append(product_name);
}
}
if(device_name.empty())
{
device_name = profile.fallback_name;
}
valid = !profile.supports_readback || ReadState();
}
RoyuanKeyboardController::~RoyuanKeyboardController()
{
hid_close(dev);
}
bool RoyuanKeyboardController::IsValid() const
{
return(valid);
}
std::string RoyuanKeyboardController::GetDeviceName()
{
return(device_name);
}
std::string RoyuanKeyboardController::GetSerial()
{
wchar_t serial_string[ROYUAN_HID_MAX_STR] = { 0 };
if(hid_get_serial_number_string(dev, serial_string, ROYUAN_HID_MAX_STR) != 0)
{
return("");
}
return(StringUtils::wstring_to_string(serial_string));
}
std::string RoyuanKeyboardController::GetLocation()
{
return("HID: " + location);
}
RoyuanKeyboardProtocol RoyuanKeyboardController::GetProtocol() const
{
return(profile.protocol);
}
unsigned char RoyuanKeyboardController::GetMode() const
{
return(current_mode);
}
unsigned char RoyuanKeyboardController::GetSpeed() const
{
return(current_speed);
}
unsigned char RoyuanKeyboardController::GetBrightness() const
{
return(current_brightness);
}
unsigned char RoyuanKeyboardController::GetRed() const
{
return(current_red);
}
unsigned char RoyuanKeyboardController::GetGreen() const
{
return(current_green);
}
unsigned char RoyuanKeyboardController::GetBlue() const
{
return(current_blue);
}
unsigned char RoyuanKeyboardController::CalculateChecksum(const unsigned char* data, std::size_t length) const
{
unsigned int sum = 0;
for(std::size_t index = 0; index < length; index++)
{
sum += data[index];
}
if(profile.checksum_type == RoyuanChecksumType::OnesComplement)
{
return(static_cast<unsigned char>(0xFF - (sum & 0xFF)));
}
unsigned int next_power_of_two = 1;
while(next_power_of_two < sum)
{
next_power_of_two <<= 1;
}
return(static_cast<unsigned char>((next_power_of_two - sum - 1) & 0xFF));
}
bool RoyuanKeyboardController::ReadState()
{
unsigned char request[ROYUAN_REPORT_LENGTH + 1] = { 0x00 };
request[1] = ROYUAN_COMMAND_GET_LED_PARAMS;
request[8] = CalculateChecksum(&request[1], 7);
int result = hid_send_feature_report(dev, request, sizeof(request));
if(result < 0)
{
LOG_DEBUG("[ROYUAN]: GET_LEDPARAM request failed: %ls", hid_error(dev));
return(false);
}
for(unsigned int attempt = 0; attempt < 5; attempt++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(80));
unsigned char response[ROYUAN_REPORT_LENGTH + 1] = { 0x00 };
result = hid_get_feature_report(dev, response, sizeof(response));
if(result > 8 && response[1] == ROYUAN_COMMAND_GET_LED_PARAMS)
{
current_mode = response[2];
current_speed = response[3] <= profile.speed_max ? profile.speed_max - response[3] : profile.speed_default;
current_brightness = std::min<unsigned char>(response[4], profile.brightness_max);
current_option = response[5];
current_red = response[6];
current_green = response[7];
current_blue = response[8];
return(true);
}
}
LOG_DEBUG("[ROYUAN]: Device did not answer GET_LEDPARAM");
return(false);
}
bool RoyuanKeyboardController::SendUpdate()
{
unsigned char buffer[ROYUAN_REPORT_LENGTH + 1] = { 0x00 };
buffer[1] = ROYUAN_COMMAND_SET_LED_PARAMS;
buffer[2] = current_mode;
if(profile.reverse_speed)
{
buffer[3] = profile.speed_max - std::min<unsigned char>(current_speed, profile.speed_max);
}
else
{
buffer[3] = std::min<unsigned char>(current_speed, profile.speed_max);
}
buffer[4] = std::min<unsigned char>(current_brightness, profile.brightness_max);
buffer[5] = current_option | current_dazzle;
buffer[6] = current_red;
buffer[7] = current_green;
buffer[8] = current_blue;
buffer[9] = CalculateChecksum(&buffer[1], 8);
int result = hid_send_feature_report(dev, buffer, sizeof(buffer));
if(result < 0)
{
LOG_ERROR("[ROYUAN]: SET_LEDPARAM failed: %ls", hid_error(dev));
return(false);
}
return(true);
}
void RoyuanKeyboardController::SetDazzle(bool is_dazzle)
{
if(profile.protocol == RoyuanKeyboardProtocol::Legacy)
{
current_dazzle = is_dazzle ? 0x08 : 0x07;
}
}
void RoyuanKeyboardController::SetOption(unsigned char option)
{
current_option = option;
}
void RoyuanKeyboardController::SetMode(unsigned char mode, unsigned char speed, unsigned char brightness)
{
current_mode = mode;
current_speed = std::min<unsigned char>(speed, profile.speed_max);
current_brightness = std::min<unsigned char>(brightness, profile.brightness_max);
if(profile.protocol == RoyuanKeyboardProtocol::AkkoBSeries)
{
current_option = profile.option_default;
}
SendUpdate();
}
void RoyuanKeyboardController::SetColor(unsigned char red, unsigned char green, unsigned char blue)
{
current_red = red;
current_green = green;
current_blue = blue;
if(profile.protocol == RoyuanKeyboardProtocol::AkkoBSeries)
{
current_option = profile.option_default;
}
SendUpdate();
}
@@ -1,112 +0,0 @@
/*---------------------------------------------------------*\
| RoyuanKeyboardController.h |
| |
| Driver for ROYUAN-based keyboard lighting controllers |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#include <hidapi.h>
#define ROYUAN_REPORT_LENGTH 64
#define ROYUAN_HID_MAX_STR 255
#define ROYUAN_COMMAND_SET_LED_PARAMS 0x07
#define ROYUAN_COMMAND_GET_LED_PARAMS 0x87
#define ROYUAN_AKKO_EFFECT_OFF 0x00
#define ROYUAN_AKKO_EFFECT_STATIC 0x01
#define ROYUAN_AKKO_EFFECT_BREATHING 0x02
#define ROYUAN_AKKO_EFFECT_WAVE 0x04
#define ROYUAN_AKKO_EFFECT_RIPPLE 0x05
#define ROYUAN_AKKO_EFFECT_REACTIVE 0x08
#define ROYUAN_AKKO_SPEED_MIN 0x00
#define ROYUAN_AKKO_SPEED_MAX 0x04
#define ROYUAN_AKKO_SPEED_DEFAULT 0x00
#define ROYUAN_AKKO_BRIGHTNESS_MIN 0x00
#define ROYUAN_AKKO_BRIGHTNESS_MAX 0x04
#define ROYUAN_AKKO_BRIGHTNESS_DEFAULT 0x03
enum class RoyuanKeyboardProtocol
{
Legacy,
AkkoBSeries
};
enum class RoyuanChecksumType
{
NextPowerOfTwo,
OnesComplement
};
struct RoyuanKeyboardProfile
{
RoyuanKeyboardProtocol protocol;
RoyuanChecksumType checksum_type;
unsigned char speed_max;
unsigned char speed_default;
unsigned char brightness_max;
unsigned char brightness_default;
unsigned char option_default;
unsigned char dazzle_default;
bool reverse_speed;
bool supports_readback;
std::string fallback_name;
static RoyuanKeyboardProfile EpomakerLegacy();
static RoyuanKeyboardProfile AkkoBSeries();
};
class RoyuanKeyboardController
{
public:
RoyuanKeyboardController(hid_device* dev_handle, char* path, const RoyuanKeyboardProfile& profile);
virtual ~RoyuanKeyboardController();
bool IsValid() const;
std::string GetDeviceName();
std::string GetSerial();
std::string GetLocation();
RoyuanKeyboardProtocol GetProtocol() const;
unsigned char GetMode() const;
unsigned char GetSpeed() const;
unsigned char GetBrightness() const;
unsigned char GetRed() const;
unsigned char GetGreen() const;
unsigned char GetBlue() const;
void SetDazzle(bool is_dazzle);
void SetOption(unsigned char option);
void SetMode(unsigned char mode, unsigned char speed, unsigned char brightness);
void SetColor(unsigned char red, unsigned char green, unsigned char blue);
private:
std::string device_name;
std::string location;
hid_device* dev;
RoyuanKeyboardProfile profile;
bool valid;
unsigned char current_mode;
unsigned char current_speed;
unsigned char current_brightness;
unsigned char current_dazzle;
unsigned char current_option;
unsigned char current_red;
unsigned char current_green;
unsigned char current_blue;
unsigned char CalculateChecksum(const unsigned char* data, std::size_t length) const;
bool ReadState();
bool SendUpdate();
};
@@ -1,69 +0,0 @@
/*---------------------------------------------------------*\
| RoyuanKeyboardControllerDetect.cpp |
| |
| Detector for ROYUAN-based keyboard lighting controllers |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "EpomakerController.h"
#include "RGBController_AkkoKeyboard.h"
#include "RGBController_EpomakerController.h"
#include "RoyuanKeyboardController.h"
#define ROYUAN_KEYBOARD_VID 0x3151
#define ROYUAN_AKKO_B_SERIES_PID 0x4003
#define ROYUAN_EPOMAKER_TH80_PRO_USB_PID 0x4010
#define ROYUAN_EPOMAKER_TH80_PRO_DONGLE_PID 0x4011
#define ROYUAN_ATTACKSHARK_K86_USB_PID 0x4015
DetectedControllers DetectRoyuanAkkoKeyboardControllers(hid_device_info* info, const std::string&)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
RoyuanKeyboardController* controller = new RoyuanKeyboardController(dev, info->path, RoyuanKeyboardProfile::AkkoBSeries());
if(controller->IsValid())
{
detected_controllers.push_back(new RGBController_AkkoKeyboard(controller));
}
else
{
delete controller;
}
}
return(detected_controllers);
}
DetectedControllers DetectRoyuanLegacyKeyboardControllers(hid_device_info* info, const std::string&)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
EpomakerController* controller = new EpomakerController(dev, info->path);
RGBController_EpomakerController* rgb_controller = new RGBController_EpomakerController(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_I("Akko 3068B Plus", DetectRoyuanAkkoKeyboardControllers, ROYUAN_KEYBOARD_VID, ROYUAN_AKKO_B_SERIES_PID, 0);
REGISTER_HID_DETECTOR_I("Epomaker TH80 Pro (USB Cable)", DetectRoyuanLegacyKeyboardControllers, ROYUAN_KEYBOARD_VID, ROYUAN_EPOMAKER_TH80_PRO_USB_PID, 2);
REGISTER_HID_DETECTOR_I("Epomaker TH80 Pro (USB Dongle)", DetectRoyuanLegacyKeyboardControllers, ROYUAN_KEYBOARD_VID, ROYUAN_EPOMAKER_TH80_PRO_DONGLE_PID, 2);
REGISTER_HID_DETECTOR_I("Attack Shark K86 (USB Cable)", DetectRoyuanLegacyKeyboardControllers, ROYUAN_KEYBOARD_VID, ROYUAN_ATTACKSHARK_K86_USB_PID, 2);
@@ -22,9 +22,6 @@ SteelSeriesAeroxWirelessController::SteelSeriesAeroxWirelessController(hid_devic
case AEROX_3_WIRELESS_WIRED:
name = STEELSERIES_AEROX3_WIRELESS_WIRED_NAME;
break;
case AEROX_3_GEN2_WIRELESS:
name = STEELSERIES_AEROX3_GEN2_WIRELESS_NAME;
break;
case AEROX_5_WIRELESS:
name = STEELSERIES_AEROX5_WIRELESS_NAME;
break;
@@ -78,7 +75,6 @@ bool SteelSeriesAeroxWirelessController::IsWireless()
switch(proto)
{
case AEROX_3_WIRELESS:
case AEROX_3_GEN2_WIRELESS:
case AEROX_5_WIRELESS:
case AEROX_5_DESTINY_WIRELESS:
case AEROX_5_DIABLO_WIRELESS:
@@ -20,7 +20,6 @@
#define STEELSERIES_AEROX3_WIRELESS_NAME "SteelSeries Aerox 3 Wireless (2.4 GHz wireless mode)"
#define STEELSERIES_AEROX3_WIRELESS_WIRED_NAME "SteelSeries Aerox 3 Wireless (wired mode)"
#define STEELSERIES_AEROX3_GEN2_WIRELESS_NAME "SteelSeries Aerox 3 Wireless Gen 2 (2.4 GHz wireless mode)"
#define STEELSERIES_AEROX5_WIRELESS_NAME "SteelSeries Aerox 5 Wireless (2.4 GHz wireless mode)"
#define STEELSERIES_AEROX5_WIRELESS_WIRED_NAME "SteelSeries Aerox 5 Wireless (wired mode)"
#define STEELSERIES_AEROX5_DESTINY_WIRELESS_NAME "SteelSeries Aerox 5 Wireless Destiny 2 Edition (2.4 GHz wireless mode)"
@@ -150,18 +150,3 @@ std::string SteelSeriesApexBaseController::GetVersion()
return(return_string);
}
bool SteelSeriesApexBaseController::SupportsBrightness()
{
return(false);
}
unsigned char SteelSeriesApexBaseController::GetBrightness()
{
return(APEX_BRIGHTNESS_MAX);
}
void SteelSeriesApexBaseController::SetBrightness(unsigned char /*value*/)
{
}
@@ -19,7 +19,6 @@
#define STEELSERIES_PACKET_IN_SIZE 64
#define STEELSERIES_PACKET_OUT_SIZE STEELSERIES_PACKET_IN_SIZE + 1
#define STEELSERIES_APEX_HID_TIMEOUT 100
/*-------------------------------------------------*\
| Protocol quirk applicability |
@@ -59,17 +58,8 @@ enum
APEX_2023_PACKET_ID_INIT = 0x4B, /* New Initialization */
APEX_2023_PACKET_ID_ONBOARD = 0x41, /* New Wired onboard mode */
APEX_2023_PACKET_ID_REGION = 0xF5, /* Region byte */
APEX_PACKET_ID_SET_BRIGHTNESS = 0x23, /* Illumination brightness */
APEX_PACKET_ID_GET_BRIGHTNESS = 0xA3, /* Brightness read back */
};
/*---------------------------------------------------------*\
| Range of the illumination brightness setting, the same |
| range the brightness keys on the keyboard step through |
\*---------------------------------------------------------*/
#define APEX_BRIGHTNESS_MIN 0
#define APEX_BRIGHTNESS_MAX 10
class SteelSeriesApexBaseController
{
public:
@@ -85,14 +75,6 @@ public:
virtual void SetLEDsDirect(std::vector<RGBColor> colors) = 0;
/*-----------------------------------------------------*\
| Illumination brightness, offered only by the models |
| that answer the read back request |
\*-----------------------------------------------------*/
virtual bool SupportsBrightness();
virtual unsigned char GetBrightness();
virtual void SetBrightness(unsigned char /*value*/);
steelseries_type proto_type;
protected:
@@ -65,20 +65,6 @@ RGBController_SteelSeriesApex::RGBController_SteelSeriesApex(SteelSeriesApexBase
Onboard.value = APEX_MODE_ONBOARD;
Onboard.flags = 0;
Onboard.color_mode = MODE_COLORS_NONE;
/*---------------------------------------------------------*\
| The brightness setting applies to the on-board lighting. |
| Direct mode sends absolute colors and is not affected by |
| it, so it is offered on this mode only. |
\*---------------------------------------------------------*/
if(controller->SupportsBrightness())
{
Onboard.flags |= MODE_FLAG_HAS_BRIGHTNESS;
Onboard.brightness_min = APEX_BRIGHTNESS_MIN;
Onboard.brightness_max = APEX_BRIGHTNESS_MAX;
Onboard.brightness = controller->GetBrightness();
}
modes.push_back(Onboard);
SetupZones();
@@ -157,11 +143,5 @@ void RGBController_SteelSeriesApex::DeviceUpdateSingleLED(int /*led*/)
void RGBController_SteelSeriesApex::DeviceUpdateMode()
{
std::vector<RGBColor> temp_colors;
if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS)
{
controller->SetBrightness((unsigned char)modes[active_mode].brightness);
}
controller->SetMode(modes[active_mode].value, temp_colors);
}
@@ -76,15 +76,6 @@ SteelSeriesApexController::SteelSeriesApexController(hid_device* dev_handle, ste
direct_packet_length = direct_packet_length_map.at(device_pid);
}
SendInitialization();
/*-----------------------------------------------------*\
| Illumination brightness is offered only when the |
| keyboard answers the read back request, so models |
| that do not implement it are left alone. Asked after |
| initialization, so the keyboard is in a known state |
\*-----------------------------------------------------*/
current_brightness = APEX_BRIGHTNESS_MAX;
brightness_supported = ReadBrightness(current_brightness);
}
SteelSeriesApexController::~SteelSeriesApexController()
@@ -274,80 +265,6 @@ void SteelSeriesApexController::SendInitialization()
}
}
/*---------------------------------------------------------*\
| Illumination brightness, the same setting the brightness |
| keys on the keyboard step through. It applies to the |
| on-board lighting; direct mode carries absolute colors |
| and is not affected. |
\*---------------------------------------------------------*/
bool SteelSeriesApexController::ReadBrightness(unsigned char& brightness)
{
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE];
memset(obuf, 0x00, sizeof(obuf));
memset(ibuf, 0x00, sizeof(ibuf));
obuf[0x00] = 0x00;
obuf[0x01] = APEX_PACKET_ID_GET_BRIGHTNESS;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
int result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, STEELSERIES_APEX_HID_TIMEOUT);
/*-----------------------------------------------------*\
| An unsupported request is answered with 0xFF in the |
| status byte |
\*-----------------------------------------------------*/
if(result < 3 || ibuf[0x00] != APEX_PACKET_ID_GET_BRIGHTNESS || ibuf[0x01] != 0x00)
{
return false;
}
if(ibuf[0x02] > APEX_BRIGHTNESS_MAX)
{
return false;
}
brightness = ibuf[0x02];
return true;
}
bool SteelSeriesApexController::SupportsBrightness()
{
return brightness_supported;
}
unsigned char SteelSeriesApexController::GetBrightness()
{
return current_brightness;
}
void SteelSeriesApexController::SetBrightness(unsigned char brightness)
{
if(!brightness_supported)
{
return;
}
if(brightness > APEX_BRIGHTNESS_MAX)
{
brightness = APEX_BRIGHTNESS_MAX;
}
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
memset(obuf, 0x00, sizeof(obuf));
obuf[0x00] = 0x00;
obuf[0x01] = APEX_PACKET_ID_SET_BRIGHTNESS;
obuf[0x02] = brightness;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
current_brightness = brightness;
}
std::string SteelSeriesApexController::GetSerial()
{
if(kbd_quirk == APEX_GEN1)
@@ -30,17 +30,10 @@ public:
void SetLEDsDirect(std::vector<RGBColor> colors) override;
std::string GetSerial() override;
bool SupportsBrightness() override;
unsigned char GetBrightness() override;
void SetBrightness(unsigned char brightness) override;
private:
void SelectProfile(unsigned char profile);
void SendInitialization();
void SendDeinitialization();
bool ReadBrightness(unsigned char& brightness);
unsigned int direct_packet_length;
bool brightness_supported;
unsigned char current_brightness;
unsigned int direct_packet_length;
};
@@ -22,7 +22,6 @@
#include "SteelSeriesQCKMatController.h"
#include "SteelSeriesRivalController.h"
#include "SteelSeriesRival3Controller.h"
#include "SteelSeriesRival5Controller.h"
#include "SteelSeriesSenseiController.h"
#include "SteelSeriesSiberiaController.h"
#include "RGBController_SteelSeriesArctisNova3.h"
@@ -33,7 +32,6 @@
#include "RGBController_SteelSeriesQCKMat.h"
#include "RGBController_SteelSeriesRival.h"
#include "RGBController_SteelSeriesRival3.h"
#include "RGBController_SteelSeriesRival5.h"
#include "RGBController_SteelSeriesSensei.h"
#include "RGBController_SteelSeriesSiberia.h"
@@ -85,11 +83,6 @@ DetectedControllers DetectSteelSeriesAerox3WirelessWired(hid_device_info* info,
return(DetectSteelSeriesAeroxWireless(info, name, AEROX_3_WIRELESS_WIRED));
}
DetectedControllers DetectSteelSeriesAerox3Gen2Wireless(hid_device_info* info, const std::string& name)
{
return(DetectSteelSeriesAeroxWireless(info, name, AEROX_3_GEN2_WIRELESS));
}
DetectedControllers DetectSteelSeriesAerox5(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
@@ -436,32 +429,6 @@ DetectedControllers DetectSteelSeriesArctis5(hid_device_info* info, const std::s
return(detected_controllers);
}
DetectedControllers DetectSteelSeriesRival5(
hid_device_info* info,
const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev = hid_open_path(info->path);
if(dev)
{
SteelSeriesRival5Controller* controller =
new SteelSeriesRival5Controller(
dev,
RIVAL_5,
info->path,
name
);
RGBController_SteelSeriesRival5* rgb_controller =
new RGBController_SteelSeriesRival5(controller);
detected_controllers.push_back(rgb_controller);
}
return detected_controllers;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------*\
| Mice |
@@ -470,8 +437,7 @@ REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 Wireless",
REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 Wireless Wired", DetectSteelSeriesAerox3WirelessWired, STEELSERIES_VID, STEELSERIES_AEROX_3_WIRELESS_WIRED_PID, 3, 0xFFC0, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 CS2 Dragon Lore Edition Wireless", DetectSteelSeriesAerox3Wireless, STEELSERIES_VID, STEELSERIES_AEROX_3_CS2_WIRELESS_PID, 3, 0xFFC0, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 CS2 Dragon Lore Edition Wireless Wired", DetectSteelSeriesAerox3WirelessWired, STEELSERIES_VID, STEELSERIES_AEROX_3_CS2_WIRELESS_WIRED_PID, 3, 0xFFC0, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 Wireless Gen 2", DetectSteelSeriesAerox3Gen2Wireless, STEELSERIES_VID, STEELSERIES_AEROX_3_GEN2_WIRELESS_PID, 3, 0xFFC0, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 Wired", DetectSteelSeriesAerox3, STEELSERIES_VID, STEELSERIES_AEROX_3_PID, 3, 0xFFC0, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 Wired", DetectSteelSeriesAerox3, STEELSERIES_VID, STEELSERIES_AEROX_3_PID, 3, 0xFFC0, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 5 Wireless", DetectSteelSeriesAerox5Wireless, STEELSERIES_VID, STEELSERIES_AEROX_5_WIRELESS_PID, 3, 0xFFC0, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 5 Wireless Wired", DetectSteelSeriesAerox5WirelessWired, STEELSERIES_VID, STEELSERIES_AEROX_5_WIRELESS_WIRED_PID, 3, 0xFFC0, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 5 Destiny 2 Edition Wireless", DetectSteelSeriesAerox5DestinyWireless, STEELSERIES_VID, STEELSERIES_AEROX_5_DESTINY_WIRELESS_PID, 3, 0xFFC0, 1 );
@@ -505,7 +471,6 @@ REGISTER_HID_DETECTOR_I("SteelSeries Rival 700",
REGISTER_HID_DETECTOR_I("SteelSeries Rival 710", DetectSteelSeriesRival700, STEELSERIES_VID, STEELSERIES_RIVAL_710_PID, 0);
REGISTER_HID_DETECTOR_I("SteelSeries Rival 3 (Old Firmware)", DetectSteelSeriesRival3, STEELSERIES_VID, STEELSERIES_RIVAL_3_OLD_PID, 3);
REGISTER_HID_DETECTOR_I("SteelSeries Rival 3", DetectSteelSeriesRival3, STEELSERIES_VID, STEELSERIES_RIVAL_3_PID, 3);
REGISTER_HID_DETECTOR_IPU("SteelSeries Rival 5", DetectSteelSeriesRival5, STEELSERIES_VID, STEELSERIES_RIVAL_5_PID, 0, 0xFFC0, 1 );
REGISTER_HID_DETECTOR_I("SteelSeries Sensei TEN", DetectSteelSeriesSensei, STEELSERIES_VID, STEELSERIES_SENSEI_TEN_PID, 0);
REGISTER_HID_DETECTOR_I("SteelSeries Sensei TEN CS:GO Neon Rider Edition", DetectSteelSeriesSensei, STEELSERIES_VID, STEELSERIES_SENSEI_TEN_CSGO_NEON_RIDER_PID, 0);
REGISTER_HID_DETECTOR_I("SteelSeries Sensei 310", DetectSteelSeriesSensei, STEELSERIES_VID, STEELSERIES_SENSEI_310_PID, 0);
@@ -22,7 +22,6 @@
#define STEELSERIES_AEROX_3_WIRELESS_WIRED_PID 0x183A
#define STEELSERIES_AEROX_3_CS2_WIRELESS_PID 0x1878
#define STEELSERIES_AEROX_3_CS2_WIRELESS_WIRED_PID 0x187A
#define STEELSERIES_AEROX_3_GEN2_WIRELESS_PID 0x1890
#define STEELSERIES_AEROX_5_WIRELESS_PID 0x1852
#define STEELSERIES_AEROX_5_WIRELESS_WIRED_PID 0x1854
#define STEELSERIES_AEROX_5_DESTINY_WIRELESS_PID 0x185C
@@ -49,7 +48,6 @@
#define STEELSERIES_RIVAL_310_PID 0x1720
#define STEELSERIES_RIVAL_310_CSGO_HOWL_PID 0x171E
#define STEELSERIES_RIVAL_310_PUBG_PID 0x1736
#define STEELSERIES_RIVAL_5_PID 0x183C
#define STEELSERIES_RIVAL_600_PID 0x1724
#define STEELSERIES_RIVAL_600_DOTA_2_PID 0x172E
#define STEELSERIES_RIVAL_650_PID 0x172B
@@ -137,6 +135,4 @@ typedef enum
AEROX_5_DIABLO_WIRELESS_WIRED = 0x15,
AEROX_9_WIRELESS = 0x16,
AEROX_9_WIRELESS_WIRED = 0x17,
AEROX_3_GEN2_WIRELESS = 0x18,
RIVAL_5 = 0x19
} steelseries_type;
@@ -30,9 +30,7 @@ enum
STEELSERIES_MOUSE_EFFECT_BREATHING_MIN = 0x03,
STEELSERIES_MOUSE_EFFECT_DIRECT = 0x04,
STEELSERIES_MOUSE_EFFECT_RAINBOW_BREATHING = 0x05,
STEELSERIES_MOUSE_EFFECT_DISCO = 0x06,
STEELSERIES_MOUSE_EFFECT_REACTIVE = 0x07,
STEELSERIES_MOUSE_EFFECT_RAINBOW = 0x08
STEELSERIES_MOUSE_EFFECT_DISCO = 0x06
};
typedef struct
@@ -1,159 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_SteelSeriesRival5.cpp |
| |
| RGBController for SteelSeries Rival 5 |
| |
| A Ingram (Woodsy900) 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_SteelSeriesRival5.h"
RGBController_SteelSeriesRival5::RGBController_SteelSeriesRival5(SteelSeriesMouseController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "SteelSeries";
type = DEVICE_TYPE_MOUSE;
description = "SteelSeries Mouse Device";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
version = controller->GetFirmwareVersion();
mode Direct;
Direct.name = "Direct";
Direct.value = STEELSERIES_MOUSE_EFFECT_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.brightness_min = 0x00;
Direct.brightness_max = STEELSERIES_MOUSE_BRIGHTNESS_MAX;
Direct.brightness = STEELSERIES_MOUSE_BRIGHTNESS_MAX;
mode Reactive;
Reactive.name = "Reactive";
Reactive.value = STEELSERIES_MOUSE_EFFECT_REACTIVE; // any unique value; define e.g. 0x26
Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC;
Reactive.colors_min = 1;
Reactive.colors_max = 1;
Reactive.colors.resize(1);
Reactive.colors[0] = ToRGBColor(255, 255, 255);
mode Rainbow;
Rainbow.name = "Rainbow";
Rainbow.value = STEELSERIES_MOUSE_EFFECT_RAINBOW;
Rainbow.color_mode = MODE_COLORS_NONE;
Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS;
Rainbow.brightness_min = 0x00;
Rainbow.brightness_max = STEELSERIES_MOUSE_BRIGHTNESS_MAX;
Rainbow.brightness = STEELSERIES_MOUSE_BRIGHTNESS_MAX;
steelseries_mouse mouse = controller->GetMouse();
for(const uint8_t i: mouse.modes)
{
switch(i)
{
case STEELSERIES_MOUSE_EFFECT_DIRECT:
modes.push_back(Direct);
break;
case STEELSERIES_MOUSE_EFFECT_REACTIVE:
modes.push_back(Reactive);
break;
case STEELSERIES_MOUSE_EFFECT_RAINBOW:
modes.push_back(Rainbow);
break;
}
}
SetupZones();
}
RGBController_SteelSeriesRival5::~RGBController_SteelSeriesRival5()
{
Shutdown();
delete controller;
}
void RGBController_SteelSeriesRival5::SetupZones()
{
steelseries_mouse mouse = controller->GetMouse();
zone lighting_zone;
lighting_zone.name = "Whole Mouse";
lighting_zone.type = ZONE_TYPE_LINEAR;
lighting_zone.leds_min = static_cast<unsigned int>(mouse.leds.size());
lighting_zone.leds_max = static_cast<unsigned int>(mouse.leds.size());
lighting_zone.leds_count = static_cast<unsigned int>(mouse.leds.size());
zones.push_back(lighting_zone);
for(const led_info& info : mouse.leds)
{
led mouse_led;
mouse_led.name = info.name;
mouse_led.value = info.value;
leds.push_back(mouse_led);
}
SetupColors();
}
void RGBController_SteelSeriesRival5::DeviceUpdateLEDs()
{
SteelSeriesRival5Controller* ctrl = static_cast<SteelSeriesRival5Controller*>(controller);
ctrl->SetBrightness((uint8_t)modes[active_mode].brightness); // guarded, so ~free under effects
ctrl->UpdateLEDs(colors);
}
void RGBController_SteelSeriesRival5::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_SteelSeriesRival5::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_SteelSeriesRival5::DeviceUpdateMode()
{
SteelSeriesRival5Controller* ctrl = static_cast<SteelSeriesRival5Controller*>(controller);
if(modes[active_mode].name == "Reactive")
{
RGBColor c = modes[active_mode].colors[0];
ctrl->SetReactiveColor(
true,
RGBGetRValue(c),
RGBGetGValue(c),
RGBGetBValue(c)
);
}
else if(modes[active_mode].name == "Rainbow")
{
ctrl->SetRainbowEffect(true);
}
else
{
ctrl->SetReactiveColor(false,0,0,0); // leaving Reactive must disable it (it's persistent in HW)
DeviceUpdateLEDs();
}
}
void RGBController_SteelSeriesRival5::DeviceSaveMode()
{
/*--------------------------------------------------------*\
| Note: Rival 5 does not have a "save" command |
| The Rival 5 saves the current state automatically |
\*--------------------------------------------------------*/
}
@@ -1,32 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_SteelSeriesRival5.h |
| |
| RGBController for SteelSeries Rival 5 |
| |
| A Ingram (Woodsy900) 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "SteelSeriesRival5Controller.h"
class RGBController_SteelSeriesRival5 : public RGBController
{
public:
RGBController_SteelSeriesRival5(SteelSeriesMouseController* controller_ptr);
~RGBController_SteelSeriesRival5();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
SteelSeriesMouseController* controller;
};
@@ -1,174 +0,0 @@
/*---------------------------------------------------------*\
| SteelSeriesRival5Controller.cpp |
| |
| Controller for SteelSeries Rival 5 |
| |
| A Ingram (Woodsy900) 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "SteelSeriesRival5Controller.h"
#include "LogManager.h"
#include <cstring>
SteelSeriesRival5Controller::SteelSeriesRival5Controller(
hid_device* dev_handle,
steelseries_type proto_type,
const char* path,
std::string dev_name
)
: SteelSeriesMouseController(
dev_handle,
proto_type,
path,
dev_name
)
{
memset(zone_colors, 0, sizeof(zone_colors));
}
SteelSeriesRival5Controller::~SteelSeriesRival5Controller()
{
hid_close(dev);
}
steelseries_mouse SteelSeriesRival5Controller::GetMouse()
{
return rival_5;
}
std::string SteelSeriesRival5Controller::GetFirmwareVersion()
{
return "";
/*--------------------------------------------------------*\
| Note: Rival 5 does not have a "Get firmware" command |
\*--------------------------------------------------------*/
}
void SteelSeriesRival5Controller::SetLightEffectAll(uint8_t)
{
/*--------------------------------------------------------*\
| Note: Not Used |
\*--------------------------------------------------------*/
}
void SteelSeriesRival5Controller::SetRainbowEffect(bool enable)
{
uint8_t report[64] = {0};
if(enable)
{
report[1] = 0x22;
report[2] = 0xFF;
report[3] = 0x03;
}
hid_send_feature_report(
dev,
report,
sizeof(report)
);
}
void SteelSeriesRival5Controller::Save()
{
/*--------------------------------------------------------*\
| Note: Rival 5 does not have a "save" command |
| The Rival 5 saves the current state automatically |
\*--------------------------------------------------------*/
}
void SteelSeriesRival5Controller::SetColor(
unsigned char zone_id,
unsigned char red,
unsigned char green,
unsigned char blue,
unsigned char /*brightness*/
)
{
if(zone_id >= 10)
{
return;
}
zone_colors[zone_id][0] = red;
zone_colors[zone_id][1] = green;
zone_colors[zone_id][2] = blue;
}
void SteelSeriesRival5Controller::UpdateLEDs(const std::vector<RGBColor>& colors)
{
uint8_t report[64] = {0};
report[0] = 0x00;
report[1] = 0x21;
report[2] = 0xFF;
report[3] = 0x03;
for(size_t i = 0; i < 10 && i < colors.size(); i++)
{
report[4 + (i * 3) + 0] = RGBGetRValue(colors[i]);
report[4 + (i * 3) + 1] = RGBGetGValue(colors[i]);
report[4 + (i * 3) + 2] = RGBGetBValue(colors[i]);
}
int result =
hid_send_feature_report(
dev,
report,
sizeof(report)
);
}
void SteelSeriesRival5Controller::SetBrightness(uint8_t brightness)
{
if(brightness == current_brightness) return; // only on change
current_brightness = brightness;
uint8_t report[64] = {0};
report[1] = 0x23;
//report[2] = brightness;
if(brightness >= 88)
{
report[2] = 0x64;
}
else if(brightness >= 63)
{
report[2] = 0x32;
}
else if(brightness >= 38)
{
report[2] = 0x19;
}
else if(brightness >= 13)
{
report[2] = 0x0C;
}
else
{
report[2] = 0x00;
} // 0x00–0x64
hid_send_feature_report(dev, report, sizeof(report));
}
void SteelSeriesRival5Controller::SetReactiveColor(bool on, uint8_t r, uint8_t g, uint8_t b)
{
uint8_t report[64] = {0};
report[1] = 0x26;
if(on)
{
report[2] = 0x01;
report[3] = 0x00;
report[4] = r;
report[5] = g;
report[6] = b;
}
// off → bytes stay 0x00 → [00 26 00 00 00 00 00]
hid_send_feature_report(dev, report, sizeof(report));
}
@@ -1,84 +0,0 @@
/*---------------------------------------------------------*\
| SteelSeriesRival5Controller.h |
| |
| SteelSeriesRival5Controller for SteelSeries Rival 5 |
| |
| A Ingram (Woodsy900) 20 Jul 2026 |
| |
| 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 <cstring>
#include "RGBController.h"
#include "SteelSeriesDevices.h"
#include "SteelSeriesMouseController.h"
#define STEELSERIES_RIVAL5_NAME "SteelSeries Rival 5"
static const steelseries_mouse rival_5 =
{
{
0x04, // Direct
0x07, // Reactive
0x08 // Rainbow (for Rival 5, this is a static rainbow effect that cannot be changed)
},
{
{ "Scroll Wheel", 0 },
{ "Body Left 1", 1 },
{ "Body Right 1", 2 },
{ "Body Left 2", 3 },
{ "Body Right 2", 4 },
{ "Body Left 3", 5 },
{ "Body Right 3", 6 },
{ "Body Left 4", 7 },
{ "Body Right 4", 8 },
{ "Logo", 9 }
}
};
class SteelSeriesRival5Controller : public SteelSeriesMouseController
{
public:
SteelSeriesRival5Controller(
hid_device* dev_handle,
steelseries_type proto_type,
const char* path,
std::string dev_name
);
~SteelSeriesRival5Controller();
std::string GetFirmwareVersion() override;
steelseries_mouse GetMouse() override;
void Save() override;
void SetLightEffectAll(uint8_t effect) override;
void SetRainbowEffect(bool enable);
void SetBrightness(uint8_t brightness);
void SetColor(
unsigned char zone_id,
unsigned char red,
unsigned char green,
unsigned char blue,
unsigned char brightness
) override;
void SetReactiveColor(
bool on,
uint8_t r,
uint8_t g,
uint8_t b
);
void UpdateLEDs(const std::vector<RGBColor>& colors);
private:
uint8_t zone_colors[10][3];
uint8_t current_brightness = 0xFF;
};
+6 -6
View File
@@ -228,8 +228,8 @@ The Zone Data block represents one entry in the `RGBController::zones` vector.
| 2 | unsigned short | num_segments | 4 | Number of segments in zone |
| Variable | Segment Data[num_segments] | segments | 4 | See [Segment Data](#segment-data) block format table. Repeat num_segments times |
| 4 | unsigned int | zone_flags | 5 | Zone flags value |
| 4 | int | zone_active_mode | 6 | Zone active_mode field value |
| 2 | unsigned short | zone_num_modes | 6 | Number of modes in zone |
| 4 | int | zone_active_mode | 6 | Zone active_mode field value |
| Variable | Mode Data[zone_num_modes] | zone_modes | 6 | See [Mode Data](#mode-data) block format table. Repeat zone_num_modes times |
## Segment Data
@@ -393,7 +393,7 @@ The server responds with a data block containing I2C bus information.
| Size | Format | Name | Protocol Version | Description |
| -------- | -------------------------- | ----------- | ---------------- | ---------------------------------------------------------------------------- |
| 4 | unsigned int | data_size | 6 | Size of all data in packet |
| 2 | unsigned short | bus_count | 6 | Number of I2C bus entries |
| 4 | unsigned int | bus_count | 6 | Number of I2C bus entries |
| Variable | i2c_smbus_info[bus_count] | bus_info | 6 | I2C bus info data, each entry is the fixed-size `i2c_smbus_info` struct |
## NET_PACKET_ID_GET_HID_DEVICE_INFO
@@ -409,7 +409,7 @@ The server responds with a data block containing HID device information.
| Size | Format | Name | Protocol Version | Description |
| -------- | ------------------------------ | -------------- | ---------------- | -------------------------------------------------------------------------------------- |
| 4 | unsigned int | data_size | 6 | Size of all data in packet |
| 2 | unsigned short | device_count | 6 | Number of HID device entries |
| 4 | unsigned int | device_count | 6 | Number of HID device entries |
| Variable | HID Device Data[device_count] | device_data | 6 | See [HID Device Data](#hid-device-data) block format table. Repeat device_count times |
### HID Device Data
@@ -444,7 +444,7 @@ The server responds with a data block containing USB device information.
| Size | Format | Name | Protocol Version | Description |
| -------- | ------------------------------- | -------------- | ---------------- | -------------------------------------------------------------------------------------- |
| 4 | unsigned int | data_size | 6 | Size of all data in packet |
| 2 | unsigned short | device_count | 6 | Number of USB device entries |
| 4 | unsigned int | device_count | 6 | Number of USB device entries |
| Variable | USB Device Data[device_count] | device_data | 6 | See [USB Device Data](#usb-device-data) block format table. Repeat device_count times |
### USB Device Data
@@ -473,7 +473,7 @@ The server responds with a data block containing serial port information.
| Size | Format | Name | Protocol Version | Description |
| -------- | ------------------------------- | -------------- | ---------------- | ------------------------------------------------------------------------------------------ |
| 4 | unsigned int | data_size | 6 | Size of all data in packet |
| 2 | unsigned short | port_count | 6 | Number of serial port entries |
| 4 | unsigned int | port_count | 6 | Number of serial port entries |
| Variable | Serial Port Data[port_count] | port_data | 6 | See [Serial Port Data](#serial-port-data) block format table. Repeat port_count times |
### Serial Port Data
@@ -496,7 +496,7 @@ The server responds with a data block containing USB serial port information.
| Size | Format | Name | Protocol Version | Description |
| -------- | ------------------------------- | -------------- | ---------------- | ------------------------------------------------------------------------------------------ |
| 4 | unsigned int | data_size | 6 | Size of all data in packet |
| 2 | unsigned short | port_count | 6 | Number of USB serial port entries |
| 4 | unsigned int | port_count | 6 | Number of USB serial port entries |
| Variable | Serial Port Data[port_count] | port_data | 6 | See [Serial Port Data](#usb-serial-port-data) block format table. Repeat port_count times |
### USB Serial Port Data
+180 -471
View File
@@ -96,195 +96,13 @@ NetworkClient::NetworkClient()
server_reinitialize = false;
change_in_progress = false;
client_active = false;
profilemanager_thread = NULL;
ListenThread = NULL;
ConnectionThread = NULL;
ReceiveQueueThread = NULL;
receive_generation = 0;
receive_queue_thread_running = false;
}
/*---------------------------------------------------------*\
| Packets the receive queue thread claims. Everything else |
| in the queue is a synchronous reply for WaitForResponse. |
| Must match the queueing cases in ListenThreadFunction. |
\*---------------------------------------------------------*/
static bool IsCallbackPacket(unsigned int pkt_id)
{
switch(pkt_id)
{
case NET_PACKET_ID_REQUEST_CONTROLLER_COUNT:
case NET_PACKET_ID_REQUEST_CONTROLLER_DATA:
case NET_PACKET_ID_SET_SERVER_NAME:
case NET_PACKET_ID_DEVICE_LIST_UPDATED:
case NET_PACKET_ID_DETECTION_STARTED:
case NET_PACKET_ID_DETECTION_PROGRESS_CHANGED:
case NET_PACKET_ID_DETECTION_COMPLETE:
return(true);
default:
return(false);
}
}
/*---------------------------------------------------------*\
| Receive queue thread (see NetworkClient.h) |
\*---------------------------------------------------------*/
void NetworkClient::ReceiveQueueThreadFunction()
{
std::unique_lock<std::mutex> lock(receive_queue_mutex);
while(receive_queue_thread_running)
{
/*-------------------------------------------------*\
| Wait for a callback-emitting packet. Replies are |
| left in the queue for WaitForResponse to claim. |
\*-------------------------------------------------*/
receive_queue_cv.wait(lock, [this]
{
if(!receive_queue_thread_running)
{
return true;
}
for(std::list<NetworkClientListenerThreadQueueEntry>::iterator it = receive_queue.begin(); it != receive_queue.end(); it++)
{
if(it->generation != receive_generation || IsCallbackPacket(it->header.pkt_id))
{
return true;
}
}
return false;
});
if(!receive_queue_thread_running)
{
return;
}
/*-------------------------------------------------*\
| Drop what a previous connection queued |
\*-------------------------------------------------*/
for(std::list<NetworkClientListenerThreadQueueEntry>::iterator it = receive_queue.begin(); it != receive_queue.end(); )
{
if(it->generation != receive_generation)
{
delete[] it->data;
it = receive_queue.erase(it);
}
else
{
it++;
}
}
/*-------------------------------------------------*\
| Take the oldest callback-emitting packet |
\*-------------------------------------------------*/
NetworkClientListenerThreadQueueEntry entry;
bool claimed = false;
for(std::list<NetworkClientListenerThreadQueueEntry>::iterator it = receive_queue.begin(); it != receive_queue.end(); it++)
{
if(IsCallbackPacket(it->header.pkt_id))
{
entry = *it;
claimed = true;
receive_queue.erase(it);
break;
}
}
if(!claimed)
{
continue;
}
lock.unlock();
switch(entry.header.pkt_id)
{
case NET_PACKET_ID_REQUEST_CONTROLLER_COUNT:
ProcessReply_ControllerIDs(entry.header.pkt_size, entry.data);
break;
case NET_PACKET_ID_REQUEST_CONTROLLER_DATA:
ProcessReply_ControllerData(entry.header.pkt_size, entry.data, entry.header.pkt_dev_id);
break;
case NET_PACKET_ID_SET_SERVER_NAME:
ProcessRequest_ServerString(entry.header.pkt_size, entry.data);
break;
case NET_PACKET_ID_DETECTION_STARTED:
SignalNetworkClientUpdate(NETWORKCLIENT_UPDATE_REASON_DETECTION_STARTED);
break;
case NET_PACKET_ID_DETECTION_PROGRESS_CHANGED:
ProcessRequest_DetectionProgressChanged(entry.header.pkt_size, entry.data);
break;
case NET_PACKET_ID_DETECTION_COMPLETE:
SignalNetworkClientUpdate(NETWORKCLIENT_UPDATE_REASON_DETECTION_COMPLETE);
break;
case NET_PACKET_ID_DEVICE_LIST_UPDATED:
ProcessRequest_DeviceListChanged();
break;
}
delete[] entry.data;
lock.lock();
}
}
void NetworkClient::StopReceiveQueueThread()
{
{
std::lock_guard<std::mutex> lock(receive_queue_mutex);
receive_queue_thread_running = false;
for(std::list<NetworkClientListenerThreadQueueEntry>::iterator it = receive_queue.begin(); it != receive_queue.end(); it++)
{
delete[] it->data;
}
receive_queue.clear();
}
receive_queue_cv.notify_all();
if(ReceiveQueueThread)
{
ReceiveQueueThread->join();
delete ReceiveQueueThread;
ReceiveQueueThread = NULL;
}
}
NetworkClient::~NetworkClient()
{
StopClient();
StopReceiveQueueThread();
/*-----------------------------------------------------*\
| Free the controllers set aside by StopClient, plus |
| any left behind by a listener that never ran |
\*-----------------------------------------------------*/
ControllerListMutex.lock();
orphaned_controllers.insert(orphaned_controllers.end(), server_controllers.begin(), server_controllers.end());
server_controllers.clear();
ControllerListMutex.unlock();
DeleteOrphanedControllers();
}
/*---------------------------------------------------------*\
@@ -429,16 +247,6 @@ void NetworkClient::StartClient()
\*-----------------------------------------------------*/
ConnectionThread = new std::thread(&NetworkClient::ConnectionThreadFunction, this);
/*-----------------------------------------------------*\
| Start the receive queue thread. It spans |
| reconnects; entries age out by generation. |
\*-----------------------------------------------------*/
if(!ReceiveQueueThread)
{
receive_queue_thread_running = true;
ReceiveQueueThread = new std::thread(&NetworkClient::ReceiveQueueThreadFunction, this);
}
/*-----------------------------------------------------*\
| Start the ProfileManager listen thread |
\*-----------------------------------------------------*/
@@ -463,17 +271,6 @@ void NetworkClient::StopClient()
server_connected = false;
client_active = false;
/*-----------------------------------------------------*\
| Retire this connection's generation so requests still |
| waiting on a reply that is never coming wake up |
\*-----------------------------------------------------*/
{
std::lock_guard<std::mutex> lock(receive_queue_mutex);
receive_generation++;
}
receive_queue_cv.notify_all();
/*-----------------------------------------------------*\
| Shut down and close the client socket |
\*-----------------------------------------------------*/
@@ -526,36 +323,6 @@ void NetworkClient::StopClient()
SignalNetworkClientUpdate(NETWORKCLIENT_UPDATE_REASON_CLIENT_STOPPED);
}
/*---------------------------------------------------------*\
| Controller teardown functions |
\*---------------------------------------------------------*/
void NetworkClient::TakeOrphanedControllers(std::vector<RGBController*>& orphaned)
{
ControllerListMutex.lock();
orphaned.insert(orphaned.end(), orphaned_controllers.begin(), orphaned_controllers.end());
orphaned_controllers.clear();
ControllerListMutex.unlock();
}
void NetworkClient::DeleteOrphanedControllers()
{
/*-----------------------------------------------------*\
| Delete outside the mutex; the controller destructor |
| waits for callbacks that are already running |
\*-----------------------------------------------------*/
std::vector<RGBController*> orphaned;
TakeOrphanedControllers(orphaned);
for(std::size_t orphaned_idx = 0; orphaned_idx < orphaned.size(); orphaned_idx++)
{
delete orphaned[orphaned_idx];
}
}
void NetworkClient::SendRequest_ControllerData(unsigned int dev_id)
{
NetPacketHeader request_hdr;
@@ -642,59 +409,61 @@ std::vector<HIDDeviceInfo> NetworkClient::GetHIDDeviceInfo()
send_in_progress.unlock();
/*-------------------------------------------------*\
| Wait for response using the response queue |
| Wait for response |
\*-------------------------------------------------*/
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_GET_HID_DEVICE_INFO);
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
waiting_on_response_cv.wait(wait_lock);
/*-------------------------------------------------*\
| Parse response into device list |
\*-------------------------------------------------*/
if(response_entry.data != NULL)
if(response_header.pkt_id == NET_PACKET_ID_GET_HID_DEVICE_INFO && response_data_ptr != NULL)
{
unsigned short device_count = 0;
unsigned char* data_ptr = response_entry.data;
unsigned int& data_size = response_entry.header.pkt_size;
unsigned int device_count = 0;
unsigned char* data_ptr = response_data_ptr;
unsigned int& data_size = response_header.pkt_size;
unsigned int data_size_pkt;
COPY_DATA_FIELD(data_ptr, response_entry.data, data_size_pkt);
COPY_DATA_FIELD(data_ptr, response_data_ptr, data_size_pkt);
if(data_size_pkt == data_size)
{
COPY_DATA_FIELD(data_ptr, response_entry.data, device_count);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device_count);
for(unsigned short device_idx = 0; device_idx < device_count; device_idx++)
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
{
HIDDeviceInfo device;
COPY_DATA_FIELD(data_ptr, response_entry.data, device.vendor_id);
COPY_DATA_FIELD(data_ptr, response_entry.data, device.product_id);
COPY_DATA_FIELD(data_ptr, response_entry.data, device.release_number);
COPY_DATA_FIELD(data_ptr, response_entry.data, device.usage_page);
COPY_DATA_FIELD(data_ptr, response_entry.data, device.usage);
COPY_DATA_FIELD(data_ptr, response_entry.data, device.interface_number);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device.vendor_id);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device.product_id);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device.release_number);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device.usage_page);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device.usage);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device.interface_number);
unsigned short serial_number_size;
COPY_DATA_FIELD(data_ptr, response_entry.data, serial_number_size);
COPY_STRING_FIELD(data_ptr, response_entry.data, serial_number_size, device.serial_number);
COPY_DATA_FIELD(data_ptr, response_data_ptr, serial_number_size);
COPY_STRING_FIELD(data_ptr, response_data_ptr, serial_number_size, device.serial_number);
unsigned short manufacturer_string_size;
COPY_DATA_FIELD(data_ptr, response_entry.data, manufacturer_string_size);
COPY_STRING_FIELD(data_ptr, response_entry.data, manufacturer_string_size, device.manufacturer_string);
COPY_DATA_FIELD(data_ptr, response_data_ptr, manufacturer_string_size);
COPY_STRING_FIELD(data_ptr, response_data_ptr, manufacturer_string_size, device.manufacturer_string);
unsigned short product_string_size;
COPY_DATA_FIELD(data_ptr, response_entry.data, product_string_size);
COPY_STRING_FIELD(data_ptr, response_entry.data, product_string_size, device.product_string);
COPY_DATA_FIELD(data_ptr, response_data_ptr, product_string_size);
COPY_STRING_FIELD(data_ptr, response_data_ptr, product_string_size, device.product_string);
unsigned short path_size;
COPY_DATA_FIELD(data_ptr, response_entry.data, path_size);
COPY_STRING_FIELD(data_ptr, response_entry.data, path_size, device.path);
COPY_DATA_FIELD(data_ptr, response_data_ptr, path_size);
COPY_STRING_FIELD(data_ptr, response_data_ptr, path_size, device.path);
device_info.push_back(device);
}
}
COPY_DATA_ERROR:
delete[] response_entry.data;
delete[] response_data_ptr;
response_data_ptr = NULL;
}
}
@@ -718,38 +487,40 @@ std::vector<i2c_smbus_info> NetworkClient::GetI2CBusInfo()
send_in_progress.unlock();
/*-------------------------------------------------*\
| Wait for response using the response queue |
| Wait for response |
\*-------------------------------------------------*/
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_GET_I2C_BUS_INFO);
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
waiting_on_response_cv.wait(wait_lock);
/*-------------------------------------------------*\
| Parse response into bus list |
\*-------------------------------------------------*/
if(response_entry.data != NULL)
if(response_header.pkt_id == NET_PACKET_ID_GET_I2C_BUS_INFO && response_data_ptr != NULL)
{
unsigned short bus_count = 0;
unsigned char* data_ptr = response_entry.data;
unsigned int& data_size = response_entry.header.pkt_size;
unsigned int bus_count = 0;
unsigned char* data_ptr = response_data_ptr;
unsigned int& data_size = response_header.pkt_size;
unsigned int data_size_pkt;
COPY_DATA_FIELD(data_ptr, response_entry.data, data_size_pkt);
COPY_DATA_FIELD(data_ptr, response_data_ptr, data_size_pkt);
if(data_size_pkt == data_size)
{
COPY_DATA_FIELD(data_ptr, response_entry.data, bus_count);
COPY_DATA_FIELD(data_ptr, response_data_ptr, bus_count);
for(unsigned short bus_idx = 0; bus_idx < bus_count; bus_idx++)
for(unsigned int bus_idx = 0; bus_idx < bus_count; bus_idx++)
{
i2c_smbus_info bus;
COPY_DATA_FIELD(data_ptr, response_entry.data, bus);
COPY_DATA_FIELD(data_ptr, response_data_ptr, bus);
bus_info.push_back(bus);
}
}
COPY_DATA_ERROR:
delete[] response_entry.data;
delete[] response_data_ptr;
response_data_ptr = NULL;
}
}
@@ -773,39 +544,41 @@ std::vector<std::string> NetworkClient::GetSerialPorts()
send_in_progress.unlock();
/*-------------------------------------------------*\
| Wait for response using the response queue |
| Wait for response |
\*-------------------------------------------------*/
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_GET_SERIAL_PORTS);
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
waiting_on_response_cv.wait(wait_lock);
/*-------------------------------------------------*\
| Parse response into device list |
\*-------------------------------------------------*/
if(response_entry.data != NULL)
if(response_header.pkt_id == NET_PACKET_ID_GET_SERIAL_PORTS && response_data_ptr != NULL)
{
unsigned short port_count = 0;
unsigned char* data_ptr = response_entry.data;
unsigned int& data_size = response_entry.header.pkt_size;
unsigned int port_count = 0;
unsigned char* data_ptr = response_data_ptr;
unsigned int& data_size = response_header.pkt_size;
unsigned int data_size_pkt;
COPY_DATA_FIELD(data_ptr, response_entry.data, data_size_pkt);
COPY_DATA_FIELD(data_ptr, response_data_ptr, data_size_pkt);
if(data_size_pkt == data_size)
{
COPY_DATA_FIELD(data_ptr, response_entry.data, port_count);
COPY_DATA_FIELD(data_ptr, response_data_ptr, port_count);
for(unsigned short port_idx = 0; port_idx < port_count; port_idx++)
for(unsigned int port_idx = 0; port_idx < port_count; port_idx++)
{
std::string port_string;
unsigned short port_string_size;
COPY_DATA_FIELD(data_ptr, response_entry.data, port_string_size);
COPY_STRING_FIELD(data_ptr, response_entry.data, port_string_size, port_string);
COPY_DATA_FIELD(data_ptr, response_data_ptr, port_string_size);
COPY_STRING_FIELD(data_ptr, response_data_ptr, port_string_size, port_string);
serial_ports.push_back(port_string);
}
}
COPY_DATA_ERROR:
delete[] response_entry.data;
delete[] response_data_ptr;
response_data_ptr = NULL;
}
}
@@ -829,51 +602,53 @@ std::vector<USBDeviceInfo> NetworkClient::GetUSBDeviceInfo()
send_in_progress.unlock();
/*-------------------------------------------------*\
| Wait for response using the response queue |
| Wait for response |
\*-------------------------------------------------*/
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_GET_USB_DEVICE_INFO);
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
waiting_on_response_cv.wait(wait_lock);
/*-------------------------------------------------*\
| Parse response into device list |
\*-------------------------------------------------*/
if(response_entry.data != NULL)
if(response_header.pkt_id == NET_PACKET_ID_GET_USB_DEVICE_INFO && response_data_ptr != NULL)
{
unsigned short device_count = 0;
unsigned char* data_ptr = response_entry.data;
unsigned int& data_size = response_entry.header.pkt_size;
unsigned int device_count = 0;
unsigned char* data_ptr = response_data_ptr;
unsigned int& data_size = response_header.pkt_size;
unsigned int data_size_pkt;
COPY_DATA_FIELD(data_ptr, response_entry.data, data_size_pkt);
COPY_DATA_FIELD(data_ptr, response_data_ptr, data_size_pkt);
if(data_size_pkt == data_size)
{
COPY_DATA_FIELD(data_ptr, response_entry.data, device_count);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device_count);
for(unsigned short device_idx = 0; device_idx < device_count; device_idx++)
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
{
USBDeviceInfo device;
COPY_DATA_FIELD(data_ptr, response_entry.data, device.vendor_id);
COPY_DATA_FIELD(data_ptr, response_entry.data, device.product_id);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device.vendor_id);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device.product_id);
unsigned short serial_number_size;
COPY_DATA_FIELD(data_ptr, response_entry.data, serial_number_size);
COPY_STRING_FIELD(data_ptr, response_entry.data, serial_number_size, device.serial_number);
COPY_DATA_FIELD(data_ptr, response_data_ptr, serial_number_size);
COPY_STRING_FIELD(data_ptr, response_data_ptr, serial_number_size, device.serial_number);
unsigned short manufacturer_string_size;
COPY_DATA_FIELD(data_ptr, response_entry.data, manufacturer_string_size);
COPY_STRING_FIELD(data_ptr, response_entry.data, manufacturer_string_size, device.manufacturer_string);
COPY_DATA_FIELD(data_ptr, response_data_ptr, manufacturer_string_size);
COPY_STRING_FIELD(data_ptr, response_data_ptr, manufacturer_string_size, device.manufacturer_string);
unsigned short product_string_size;
COPY_DATA_FIELD(data_ptr, response_entry.data, product_string_size);
COPY_STRING_FIELD(data_ptr, response_entry.data, product_string_size, device.product_string);
COPY_DATA_FIELD(data_ptr, response_data_ptr, product_string_size);
COPY_STRING_FIELD(data_ptr, response_data_ptr, product_string_size, device.product_string);
device_info.push_back(device);
}
}
COPY_DATA_ERROR:
delete[] response_entry.data;
delete[] response_data_ptr;
response_data_ptr = NULL;
}
}
@@ -897,47 +672,49 @@ std::vector<SerialDeviceInfo> NetworkClient::GetUSBSerialPorts()
send_in_progress.unlock();
/*-------------------------------------------------*\
| Wait for response using the response queue |
| Wait for response |
\*-------------------------------------------------*/
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_GET_USB_SERIAL_PORTS);
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
waiting_on_response_cv.wait(wait_lock);
/*-------------------------------------------------*\
| Parse response into device list |
\*-------------------------------------------------*/
if(response_entry.data != NULL)
if(response_header.pkt_id == NET_PACKET_ID_GET_USB_SERIAL_PORTS && response_data_ptr != NULL)
{
unsigned short device_count = 0;
unsigned char* data_ptr = response_entry.data;
unsigned int& data_size = response_entry.header.pkt_size;
unsigned int device_count = 0;
unsigned char* data_ptr = response_data_ptr;
unsigned int& data_size = response_header.pkt_size;
unsigned int data_size_pkt;
COPY_DATA_FIELD(data_ptr, response_entry.data, data_size_pkt);
COPY_DATA_FIELD(data_ptr, response_data_ptr, data_size_pkt);
if(data_size_pkt == data_size)
{
COPY_DATA_FIELD(data_ptr, response_entry.data, device_count);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device_count);
for(unsigned short device_idx = 0; device_idx < device_count; device_idx++)
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
{
SerialDeviceInfo device;
COPY_DATA_FIELD(data_ptr, response_entry.data, device.vendor_id);
COPY_DATA_FIELD(data_ptr, response_entry.data, device.product_id);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device.vendor_id);
COPY_DATA_FIELD(data_ptr, response_data_ptr, device.product_id);
unsigned short port_path_size;
COPY_DATA_FIELD(data_ptr, response_entry.data, port_path_size);
COPY_STRING_FIELD(data_ptr, response_entry.data, port_path_size, device.port_path);
COPY_DATA_FIELD(data_ptr, response_data_ptr, port_path_size);
COPY_STRING_FIELD(data_ptr, response_data_ptr, port_path_size, device.port_path);
unsigned short usb_path_size;
COPY_DATA_FIELD(data_ptr, response_entry.data, usb_path_size);
COPY_STRING_FIELD(data_ptr, response_entry.data, usb_path_size, device.usb_path);
COPY_DATA_FIELD(data_ptr, response_data_ptr, usb_path_size);
COPY_STRING_FIELD(data_ptr, response_data_ptr, usb_path_size, device.usb_path);
device_info.push_back(device);
}
}
COPY_DATA_ERROR:
delete[] response_entry.data;
delete[] response_data_ptr;
response_data_ptr = NULL;
}
}
@@ -997,21 +774,23 @@ unsigned int NetworkClient::LogManager_GetLogLevel()
send_in_progress.unlock();
/*-----------------------------------------------------*\
| Wait for response using the response queue |
| Wait for response |
\*-----------------------------------------------------*/
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_LOGMANAGER_GET_LOG_LEVEL);
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
waiting_on_response_cv.wait(wait_lock);
/*-----------------------------------------------------*\
| Parse response into bus list |
\*-----------------------------------------------------*/
if(response_entry.data != NULL)
if(response_header.pkt_id == NET_PACKET_ID_LOGMANAGER_GET_LOG_LEVEL && response_data_ptr != NULL)
{
if(response_entry.header.pkt_size >= sizeof(log_level))
if(response_header.pkt_size >= sizeof(log_level))
{
memcpy(&log_level, response_entry.data, sizeof(log_level));
memcpy(&log_level, response_data_ptr, sizeof(log_level));
}
delete[] response_entry.data;
delete[] response_data_ptr;
response_data_ptr = NULL;
}
return(log_level);
@@ -1107,22 +886,24 @@ std::string NetworkClient::ProfileManager_DownloadProfile(std::string profile_na
send_in_progress.unlock();
/*-----------------------------------------------------*\
| Wait for response using the response queue |
| Wait for response |
\*-----------------------------------------------------*/
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_PROFILEMANAGER_DOWNLOAD_PROFILE);
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
waiting_on_response_cv.wait(wait_lock);
/*-----------------------------------------------------*\
| Parse response into bus list |
\*-----------------------------------------------------*/
if(response_entry.data != NULL)
if(response_header.pkt_id == NET_PACKET_ID_PROFILEMANAGER_DOWNLOAD_PROFILE && response_data_ptr != NULL)
{
unsigned int& data_size = response_entry.header.pkt_size;
unsigned char* data_ptr = response_entry.data;
unsigned int& data_size = response_header.pkt_size;
unsigned char* data_ptr = response_data_ptr;
COPY_STRING_FIELD(data_ptr, response_entry.data, data_size, response_string);
COPY_STRING_FIELD(data_ptr, response_data_ptr, data_size, response_string);
COPY_DATA_ERROR:
delete[] response_entry.data;
delete[] response_data_ptr;
response_data_ptr = NULL;
}
return(response_string);
@@ -1143,22 +924,24 @@ std::string NetworkClient::ProfileManager_GetActiveProfile()
send_in_progress.unlock();
/*-----------------------------------------------------*\
| Wait for response using the response queue |
| Wait for response |
\*-----------------------------------------------------*/
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_PROFILEMANAGER_GET_ACTIVE_PROFILE);
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
waiting_on_response_cv.wait(wait_lock);
/*-----------------------------------------------------*\
| Parse response into bus list |
\*-----------------------------------------------------*/
if(response_entry.data != NULL)
if(response_header.pkt_id == NET_PACKET_ID_PROFILEMANAGER_GET_ACTIVE_PROFILE && response_data_ptr != NULL)
{
unsigned int& data_size = response_entry.header.pkt_size;
unsigned char* data_ptr = response_entry.data;
unsigned int& data_size = response_header.pkt_size;
unsigned char* data_ptr = response_data_ptr;
COPY_STRING_FIELD(data_ptr, response_entry.data, data_size, response_string);
COPY_STRING_FIELD(data_ptr, response_data_ptr, data_size, response_string);
COPY_DATA_ERROR:
delete[] response_entry.data;
delete[] response_data_ptr;
response_data_ptr = NULL;
}
return(response_string);
@@ -1194,22 +977,23 @@ std::string NetworkClient::SettingsManager_GetSettings(std::string settings_key)
send_in_progress.unlock();
/*-----------------------------------------------------*\
| Wait for response using the response queue |
| Wait for response |
\*-----------------------------------------------------*/
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_SETTINGSMANAGER_GET_SETTINGS);
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
waiting_on_response_cv.wait(wait_lock);
/*-----------------------------------------------------*\
| Parse response into bus list |
\*-----------------------------------------------------*/
if(response_entry.data != NULL)
if(response_header.pkt_id == NET_PACKET_ID_SETTINGSMANAGER_GET_SETTINGS && response_data_ptr != NULL)
{
unsigned int& data_size = response_entry.header.pkt_size;
unsigned char* data_ptr = response_entry.data;
unsigned int& data_size = response_header.pkt_size;
unsigned char* data_ptr = response_data_ptr;
COPY_STRING_FIELD(data_ptr, response_entry.data, data_size, response_string);
COPY_STRING_FIELD(data_ptr, response_data_ptr, data_size, response_string);
COPY_DATA_ERROR:
delete[] response_entry.data;
response_data_ptr = NULL;
}
return(response_string);
@@ -1231,22 +1015,24 @@ std::string NetworkClient::SettingsManager_GetSettingsSchema(std::string setting
send_in_progress.unlock();
/*-----------------------------------------------------*\
| Wait for response using the response queue |
| Wait for response |
\*-----------------------------------------------------*/
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_SETTINGSMANAGER_GET_SETTINGS_SCHEMA);
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
waiting_on_response_cv.wait(wait_lock);
/*-----------------------------------------------------*\
| Parse response into bus list |
\*-----------------------------------------------------*/
if(response_entry.data != NULL)
if(response_header.pkt_id == NET_PACKET_ID_SETTINGSMANAGER_GET_SETTINGS_SCHEMA && response_data_ptr != NULL)
{
unsigned int& data_size = response_entry.header.pkt_size;
unsigned char* data_ptr = response_entry.data;
unsigned int& data_size = response_header.pkt_size;
unsigned char* data_ptr = response_data_ptr;
COPY_STRING_FIELD(data_ptr, response_entry.data, data_size, response_string);
COPY_STRING_FIELD(data_ptr, response_data_ptr, data_size, response_string);
COPY_DATA_ERROR:
delete[] response_entry.data;
delete[] response_data_ptr;
response_data_ptr = NULL;
}
return(response_string);
@@ -1937,16 +1723,6 @@ void NetworkClient::ListenThreadFunction()
{
LOG_INFO("[%s] Listener thread started", NETWORKCLIENT);
/*-----------------------------------------------------*\
| Claim this connection's receive generation |
\*-----------------------------------------------------*/
unsigned int listen_generation;
{
std::lock_guard<std::mutex> gen_lock(receive_queue_mutex);
listen_generation = ++receive_generation;
}
/*-----------------------------------------------------*\
| This thread handles messages received from the server |
\*-----------------------------------------------------*/
@@ -2030,30 +1806,46 @@ void NetworkClient::ListenThreadFunction()
\*-------------------------------------------------*/
switch(header.pkt_id)
{
case NET_PACKET_ID_REQUEST_CONTROLLER_COUNT:
ProcessReply_ControllerIDs(header.pkt_size, data);
break;
case NET_PACKET_ID_REQUEST_CONTROLLER_DATA:
ProcessReply_ControllerData(header.pkt_size, data, header.pkt_dev_id);
break;
case NET_PACKET_ID_REQUEST_PROTOCOL_VERSION:
ProcessReply_ProtocolVersion(header.pkt_size, data);
break;
case NET_PACKET_ID_SET_SERVER_NAME:
ProcessRequest_ServerString(header.pkt_size, data);
break;
case NET_PACKET_ID_SET_SERVER_FLAGS:
ProcessRequest_ServerFlags(header.pkt_size, data);
break;
case NET_PACKET_ID_DEVICE_LIST_UPDATED:
ProcessRequest_DeviceListChanged();
break;
case NET_PACKET_ID_DETECTION_STARTED:
SignalNetworkClientUpdate(NETWORKCLIENT_UPDATE_REASON_DETECTION_STARTED);
break;
case NET_PACKET_ID_DETECTION_PROGRESS_CHANGED:
ProcessRequest_DetectionProgressChanged(header.pkt_size, data);
break;
case NET_PACKET_ID_DETECTION_COMPLETE:
SignalNetworkClientUpdate(NETWORKCLIENT_UPDATE_REASON_DETECTION_COMPLETE);
break;
case NET_PACKET_ID_LOGMANAGER_LOGGED_ENTRY:
ProcessRequest_LogManager_LoggedEntry(header.pkt_size, data);
break;
/*---------------------------------------------*\
| Queue what is not handled inline; the queue |
| owns the data. Running callback packets here |
| would block the listener on the GUI. |
\*---------------------------------------------*/
case NET_PACKET_ID_REQUEST_CONTROLLER_COUNT:
case NET_PACKET_ID_REQUEST_CONTROLLER_DATA:
case NET_PACKET_ID_SET_SERVER_NAME:
case NET_PACKET_ID_DEVICE_LIST_UPDATED:
case NET_PACKET_ID_DETECTION_STARTED:
case NET_PACKET_ID_DETECTION_PROGRESS_CHANGED:
case NET_PACKET_ID_DETECTION_COMPLETE:
case NET_PACKET_ID_GET_I2C_BUS_INFO:
case NET_PACKET_ID_GET_HID_DEVICE_INFO:
case NET_PACKET_ID_GET_USB_DEVICE_INFO:
@@ -2063,19 +1855,15 @@ void NetworkClient::ListenThreadFunction()
case NET_PACKET_ID_PROFILEMANAGER_DOWNLOAD_PROFILE:
case NET_PACKET_ID_PROFILEMANAGER_GET_ACTIVE_PROFILE:
case NET_PACKET_ID_SETTINGSMANAGER_GET_SETTINGS:
case NET_PACKET_ID_SETTINGSMANAGER_GET_SETTINGS_SCHEMA:
{
std::lock_guard<std::mutex> lock(receive_queue_mutex);
std::unique_lock<std::mutex> lock(waiting_on_response_mutex);
NetworkClientListenerThreadQueueEntry new_entry;
new_entry.header = header;
new_entry.data = data;
new_entry.generation = listen_generation;
response_header = header;
response_data_ptr = data;
delete_data = false;
receive_queue.push_back(new_entry);
receive_queue_cv.notify_all();
delete_data = false;
lock.unlock();
waiting_on_response_cv.notify_all();
}
break;
@@ -2093,9 +1881,8 @@ void NetworkClient::ListenThreadFunction()
profilemanager_thread->queue_mutex.lock();
NetworkClientListenerThreadQueueEntry new_entry;
new_entry.header = header;
new_entry.data = data;
new_entry.generation = listen_generation;
new_entry.header = header;
profilemanager_thread->queue.push(new_entry);
profilemanager_thread->queue_mutex.unlock();
@@ -2114,7 +1901,6 @@ void NetworkClient::ListenThreadFunction()
listen_done:
LOG_INFO("[%s] Client socket has been closed", NETWORKCLIENT);
client_flags = NET_CLIENT_FLAG_SUPPORTS_RGBCONTROLLER
| NET_CLIENT_FLAG_SUPPORTS_PROFILEMANAGER
| NET_CLIENT_FLAG_SUPPORTS_SETTINGSMANAGER;
@@ -2132,39 +1918,23 @@ listen_done:
server_initialized = false;
server_connected = false;
/*-----------------------------------------------------*\
| Retire this connection's generation. Blocked |
| requests wake with a cleared response and the |
| packets this connection queued age out. |
\*-----------------------------------------------------*/
{
std::lock_guard<std::mutex> lock(receive_queue_mutex);
receive_generation++;
}
receive_queue_cv.notify_all();
/*-----------------------------------------------------*\
| Do not delete the controllers here; the front end |
| still holds pointers to them. Move them to |
| orphaned_controllers for the teardown thread to free |
\*-----------------------------------------------------*/
ControllerListMutex.lock();
orphaned_controllers.insert(orphaned_controllers.end(), server_controllers.begin(), server_controllers.end());
std::vector<RGBController *> server_controllers_copy = server_controllers;
server_controllers.clear();
for(size_t server_controller_idx = 0; server_controller_idx < server_controllers_copy.size(); server_controller_idx++)
{
delete server_controllers_copy[server_controller_idx];
}
ControllerListMutex.unlock();
/*-----------------------------------------------------*\
| Signal only an unplanned connection loss; on a |
| requested stop the caller runs the teardown |
| Client info has changed, call the callbacks |
\*-----------------------------------------------------*/
if(client_active)
{
SignalNetworkClientUpdate(NETWORKCLIENT_UPDATE_REASON_CLIENT_DISCONNECTED);
}
SignalNetworkClientUpdate(NETWORKCLIENT_UPDATE_REASON_CLIENT_DISCONNECTED);
}
/*---------------------------------------------------------*\
@@ -2188,11 +1958,7 @@ void NetworkClient::ProcessReply_ControllerData(unsigned int data_size, unsigned
| Create a new controller from the received data |
\*-------------------------------------------------*/
new_controller = new RGBController_Network(this, dev_id);
if(RGBController::SetDeviceDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), new_controller, GetProtocolVersion()) == NULL)
{
goto COPY_DATA_ERROR;
}
RGBController::SetDeviceDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), new_controller, GetProtocolVersion());
/*-------------------------------------------------*\
| Mark this controller as remote owned |
@@ -2489,10 +2255,7 @@ void NetworkClient::ProcessRequest_RGBController_SignalUpdate(unsigned int data_
| UpdateLEDs() sends color description |
\*-------------------------------------------------*/
case RGBCONTROLLER_UPDATE_REASON_UPDATELEDS:
if(RGBController::SetColorDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), controller, GetProtocolVersion()) == NULL)
{
goto COPY_DATA_ERROR;
}
RGBController::SetColorDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), controller, GetProtocolVersion());
break;
/*-------------------------------------------------*\
@@ -2509,10 +2272,7 @@ void NetworkClient::ProcessRequest_RGBController_SignalUpdate(unsigned int data_
case RGBCONTROLLER_UPDATE_REASON_SETDEVICESPECIFICCONFIGURATION:
case RGBCONTROLLER_UPDATE_REASON_SETDEVICESPECIFICZONECONFIGURATION:
default:
if(RGBController::SetDeviceDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), controller, GetProtocolVersion()) == NULL)
{
goto COPY_DATA_ERROR;
}
RGBController::SetDeviceDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), controller, GetProtocolVersion());
/*---------------------------------------------*\
| Mark this controller as remote owned |
@@ -2740,57 +2500,6 @@ std::vector<std::string>* NetworkClient::ProcessReply_ProfileList(unsigned int d
return(profile_list);
}
/*---------------------------------------------------------*\
| Response queue helper |
\*---------------------------------------------------------*/
NetworkClientListenerThreadQueueEntry NetworkClient::WaitForResponse(unsigned int expected_pkt_id)
{
NetworkClientListenerThreadQueueEntry result;
result.data = NULL;
result.generation = 0;
std::unique_lock<std::mutex> lock(receive_queue_mutex);
/*-----------------------------------------------------*\
| Wait for a reply on this connection, or for the |
| connection to drop. Without the connection test a |
| request whose reply is lost waits forever. |
\*-----------------------------------------------------*/
receive_queue_cv.wait(lock, [this, expected_pkt_id]()
{
if(!server_connected)
{
return true;
}
for(std::list<NetworkClientListenerThreadQueueEntry>::iterator it = receive_queue.begin(); it != receive_queue.end(); it++)
{
if(it->header.pkt_id == expected_pkt_id && it->generation == receive_generation)
{
return true;
}
}
return false;
});
/*-----------------------------------------------------*\
| Find and remove the matching entry from the queue. |
| The caller owns the data pointer from here. |
\*-----------------------------------------------------*/
for(std::list<NetworkClientListenerThreadQueueEntry>::iterator it = receive_queue.begin(); it != receive_queue.end(); it++)
{
if(it->header.pkt_id == expected_pkt_id && it->generation == receive_generation)
{
result = *it;
receive_queue.erase(it);
break;
}
}
return result;
}
/*---------------------------------------------------------*\
| Private helper functions |
\*---------------------------------------------------------*/
+4 -38
View File
@@ -12,7 +12,6 @@
#pragma once
#include <condition_variable>
#include <list>
#include <mutex>
#include <queue>
#include <thread>
@@ -51,7 +50,6 @@ typedef struct
{
NetPacketHeader header;
unsigned char* data;
unsigned int generation;
} NetworkClientListenerThreadQueueEntry;
typedef struct
@@ -101,12 +99,6 @@ public:
void StartClient();
void StopClient();
/*-----------------------------------------------------*\
| Controller teardown functions |
\*-----------------------------------------------------*/
void DeleteOrphanedControllers();
void TakeOrphanedControllers(std::vector<RGBController*>& orphaned);
void SendRequest_ControllerData(unsigned int dev_id);
void SendRequest_RescanDevices();
@@ -203,17 +195,10 @@ private:
unsigned int requested_controller_index;
std::mutex send_in_progress;
/*-----------------------------------------------------*\
| Every packet the listener does not handle inline. |
| Replies are claimed by WaitForResponse, callback |
| packets by the receive queue thread. Whoever |
| claims an entry frees its data. |
\*-----------------------------------------------------*/
std::list<NetworkClientListenerThreadQueueEntry>
receive_queue;
std::mutex receive_queue_mutex;
std::condition_variable receive_queue_cv;
unsigned int receive_generation;
NetPacketHeader response_header;
unsigned char* response_data_ptr;
std::mutex waiting_on_response_mutex;
std::condition_variable waiting_on_response_cv;
/*-----------------------------------------------------*\
| Client information |
@@ -248,18 +233,6 @@ private:
std::thread * ListenThread;
NetworkClientListenerThread* profilemanager_thread;
/*-----------------------------------------------------*\
| Receive queue thread. Runs the callback-emitting |
| packets, which block on the GUI thread while the |
| GUI may itself be blocked on a reply. Queue order |
| keeps plugin callbacks single threaded. |
\*-----------------------------------------------------*/
std::thread* ReceiveQueueThread;
bool receive_queue_thread_running;
void ReceiveQueueThreadFunction();
void StopReceiveQueueThread();
/*-----------------------------------------------------*\
| Callbacks |
\*-----------------------------------------------------*/
@@ -272,7 +245,6 @@ private:
\*-----------------------------------------------------*/
std::mutex ControllerListMutex;
std::vector<RGBController*> server_controllers;
std::vector<RGBController*> orphaned_controllers;
std::vector<unsigned int> server_controller_ids;
/*-----------------------------------------------------*\
@@ -324,12 +296,6 @@ private:
\*-----------------------------------------------------*/
std::vector<std::string>* ProcessReply_ProfileList(unsigned int data_size, unsigned char* data_ptr);
/*-----------------------------------------------------*\
| Response queue helper |
\*-----------------------------------------------------*/
NetworkClientListenerThreadQueueEntry
WaitForResponse(unsigned int expected_pkt_id);
/*-----------------------------------------------------*\
| Private helper functions |
\*-----------------------------------------------------*/
+29 -177
View File
@@ -97,8 +97,6 @@ NetworkClientInfo::NetworkClientInfo()
client_protocol_version = 0;
client_is_local = false;
client_is_local_client = false;
client_send_thread = nullptr;
client_send_running = false;
}
NetworkClientInfo::~NetworkClientInfo()
@@ -106,39 +104,8 @@ NetworkClientInfo::~NetworkClientInfo()
if(client_sock != INVALID_SOCKET)
{
LOG_INFO("[%s] Closing server connection: %s", NETWORKSERVER, client_ip.c_str());
/*-------------------------------------------------*\
| Stop the send thread before closing the socket. |
| SD_BOTH (not just SD_RECEIVE) unblocks a send |
| stuck on a slow client, so the join cannot hang. |
\*-------------------------------------------------*/
if(client_send_thread)
{
client_send_running = false;
shutdown(client_sock, SD_BOTH);
client_send_cv.notify_all();
client_send_thread->join();
delete client_send_thread;
client_send_thread = nullptr;
/*---------------------------------------------*\
| Free any packets still queued; the entries |
| own their data pointers. |
\*---------------------------------------------*/
for(std::list<NetworkServerClientSendQueueEntry>::iterator entry = client_send_queue.begin(); entry != client_send_queue.end(); entry++)
{
delete[] entry->data;
}
client_send_queue.clear();
client_send_coalesce.clear();
}
else
{
shutdown(client_sock, SD_RECEIVE);
}
delete client_listen_thread;
shutdown(client_sock, SD_RECEIVE);
closesocket(client_sock);
}
}
@@ -174,7 +141,6 @@ NetworkServer::NetworkServer()
plugin_manager = nullptr;
profile_manager = nullptr;
profilemanager_thread = nullptr;
settings_manager = nullptr;
}
@@ -1031,14 +997,6 @@ void NetworkServer::ConnectionThreadFunction(int socket_idx)
client_info->client_listen_thread = new std::thread(&NetworkServer::ListenThreadFunction, this, client_info);
client_info->client_listen_thread->detach();
/*-------------------------------------------------*\
| Start the per-client send thread. It is joinable |
| (not detached) so the client destructor can stop |
| and join it cleanly. |
\*-------------------------------------------------*/
client_info->client_send_running = true;
client_info->client_send_thread = new std::thread(&NetworkServer::ClientSendThreadFunction, this, client_info);
ServerClients.push_back(client_info);
ServerClientsMutex.unlock();
@@ -1110,7 +1068,7 @@ void NetworkServer::ControllerListenThread(NetworkServerControllerThread* this_t
delete[] queue_entry.data;
SendAck(queue_entry.client_info, queue_entry.header.pkt_dev_id, queue_entry.header.pkt_id, status);
SendAck(queue_entry.client_info, queue_entry.header.pkt_id, queue_entry.header.pkt_id, status);
}
}
else
@@ -1178,7 +1136,7 @@ void NetworkServer::ProfileManagerListenThread(NetworkServerControllerThread* th
delete[] queue_entry.data;
SendAck(queue_entry.client_info, queue_entry.header.pkt_dev_id, queue_entry.header.pkt_id, status);
SendAck(queue_entry.client_info, queue_entry.header.pkt_id, queue_entry.header.pkt_id, status);
}
}
}
@@ -1688,7 +1646,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetHIDDeviceInfo(NetworkClientInfo
std::vector<HIDDeviceInfo> device_info = ResourceManager::get()->GetHIDDeviceInfo();
unsigned int data_size = 0;
unsigned short device_count = (unsigned short)device_info.size();
unsigned int device_count = (unsigned int)device_info.size();
/*-----------------------------------------------------*\
| Calculate data size |
@@ -1696,7 +1654,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetHIDDeviceInfo(NetworkClientInfo
data_size += sizeof(data_size);
data_size += sizeof(device_count);
for(unsigned short device_idx = 0; device_idx < device_count; device_idx++)
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
{
data_size += sizeof(device_info[device_idx].vendor_id);
data_size += sizeof(device_info[device_idx].product_id);
@@ -1796,7 +1754,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetI2CBusInfo(NetworkClientInfo* c
std::vector<i2c_smbus_info> bus_info = ResourceManager::get()->GetI2CBusInfo();
unsigned int data_size = 0;
unsigned short bus_count = (unsigned short)bus_info.size();
unsigned int bus_count = (unsigned int)bus_info.size();
data_size += sizeof(data_size);
data_size += sizeof(bus_count);
data_size += bus_count * sizeof(i2c_smbus_info);
@@ -1810,7 +1768,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetI2CBusInfo(NetworkClientInfo* c
memcpy(data_ptr, &bus_count, sizeof(bus_count));
data_ptr += sizeof(bus_count);
for(unsigned short bus_idx = 0; bus_idx < bus_count; bus_idx++)
for(std::size_t bus_idx = 0; bus_idx < bus_info.size(); bus_idx++)
{
memcpy(data_ptr, &bus_info[bus_idx], sizeof(bus_info[bus_idx]));
data_ptr += sizeof(bus_info[bus_idx]);
@@ -1841,7 +1799,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetSerialPorts(NetworkClientInfo*
std::vector<std::string> serial_ports = ResourceManager::get()->GetSerialPorts();
unsigned int data_size = 0;
unsigned short port_count = (unsigned short)serial_ports.size();
unsigned int port_count = (unsigned int)serial_ports.size();
/*-----------------------------------------------------*\
| Calculate data size |
@@ -1849,7 +1807,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetSerialPorts(NetworkClientInfo*
data_size += sizeof(data_size);
data_size += sizeof(port_count);
for(unsigned short port_idx = 0; port_idx < port_count; port_idx++)
for(unsigned int port_idx = 0; port_idx < port_count; port_idx++)
{
data_size += sizeof(unsigned short);
data_size += (unsigned int)strlen(serial_ports[port_idx].c_str()) + 1;
@@ -1867,7 +1825,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetSerialPorts(NetworkClientInfo*
memcpy(data_ptr, &port_count, sizeof(port_count));
data_ptr += sizeof(port_count);
for(unsigned short port_idx = 0; port_idx < port_count; port_idx++)
for(unsigned int port_idx = 0; port_idx < port_count; port_idx++)
{
unsigned short port_string_size = (unsigned short)strlen(serial_ports[port_idx].c_str()) + 1;
memcpy(data_ptr, &port_string_size, sizeof(port_string_size));
@@ -1901,7 +1859,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetUSBDeviceInfo(NetworkClientInfo
std::vector<USBDeviceInfo> device_info = ResourceManager::get()->GetUSBDeviceInfo();
unsigned int data_size = 0;
unsigned short device_count = (unsigned short)device_info.size();
unsigned int device_count = (unsigned int)device_info.size();
/*-----------------------------------------------------*\
| Calculate data size |
@@ -1909,7 +1867,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetUSBDeviceInfo(NetworkClientInfo
data_size += sizeof(data_size);
data_size += sizeof(device_count);
for(unsigned short device_idx = 0; device_idx < device_count; device_idx++)
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
{
data_size += sizeof(device_info[device_idx].vendor_id);
data_size += sizeof(device_info[device_idx].product_id);
@@ -1985,7 +1943,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetUSBSerialPorts(NetworkClientInf
std::vector<SerialDeviceInfo> device_info = ResourceManager::get()->GetUSBSerialPorts();
unsigned int data_size = 0;
unsigned short device_count = (unsigned short)device_info.size();
unsigned int device_count = (unsigned int)device_info.size();
/*-----------------------------------------------------*\
| Calculate data size |
@@ -1993,7 +1951,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetUSBSerialPorts(NetworkClientInf
data_size += sizeof(data_size);
data_size += sizeof(device_count);
for(unsigned short device_idx = 0; device_idx < device_count; device_idx++)
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
{
data_size += sizeof(device_info[device_idx].vendor_id);
data_size += sizeof(device_info[device_idx].product_id);
@@ -3028,7 +2986,7 @@ NetPacketStatus NetworkServer::ProcessRequest_RGBController_SetHidden(NetworkCli
return(NET_PACKET_STATUS_ERROR_INVALID_ID);
}
controllers[controller_idx]->SetHidden(*(bool*)data_ptr);
controllers[controller_idx]->SetHidden((bool*)data_ptr);
return(NET_PACKET_STATUS_OK);
}
@@ -4085,19 +4043,15 @@ void NetworkServer::SendRequest_RGBController_SignalUpdate(RGBController * contr
if(found)
{
/*-------------------------------------------------*\
| Queue to each connected client with protocol 6+. |
| ServerClientsMutex is held across the loop so a |
| client cannot be deleted while we enqueue to it. |
| Send to each connected client with protocol 6 or |
| higher |
\*-------------------------------------------------*/
ServerClientsMutex.lock();
for(std::size_t client_idx = 0; client_idx < ServerClients.size(); client_idx++)
{
NetworkClientInfo* client_info = ServerClients[client_idx];
if(client_info->client_protocol_version >= 6)
if(ServerClients[client_idx]->client_protocol_version >= 6)
{
unsigned int protocol_version = client_info->client_protocol_version;
SOCKET client_sock = ServerClients[client_idx]->client_sock;
unsigned int protocol_version = ServerClients[client_idx]->client_protocol_version;
NetPacketHeader reply_hdr;
unsigned char* reply_data;
unsigned int reply_size;
@@ -4200,121 +4154,19 @@ void NetworkServer::SendRequest_RGBController_SignalUpdate(RGBController * contr
InitNetPacketHeader(&reply_hdr, controller_id, NET_PACKET_ID_RGBCONTROLLER_SIGNALUPDATE, reply_size);
/*-----------------------------------------*\
| Queue the packet for the send thread. |
| This runs inside SignalUpdate, so a |
| blocking send would stall the callback |
| drain and deadlock a rescan. The entry |
| takes ownership of reply_data; the send |
| thread frees it after the send, so it is |
| safe to send after the controller is |
| gone. |
| Send packet |
\*-----------------------------------------*/
{
std::lock_guard<std::mutex> queue_lock(client_info->client_send_mutex);
send_in_progress.lock();
send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL);
send(client_sock, (const char *)reply_data, reply_size, MSG_NOSIGNAL);
send_in_progress.unlock();
if(update_reason == RGBCONTROLLER_UPDATE_REASON_UPDATELEDS)
{
/*---------------------------------*\
| Coalesce: a newer frame for |
| this controller replaces the |
| pending one, keeping the queue |
| bounded under a stalled client. |
\*---------------------------------*/
std::map<unsigned int, std::list<NetworkServerClientSendQueueEntry>::iterator>::iterator existing = client_info->client_send_coalesce.find(controller_id);
if(existing != client_info->client_send_coalesce.end())
{
delete[] existing->second->data;
existing->second->header = reply_hdr;
existing->second->data = reply_data;
}
else
{
client_info->client_send_queue.push_back(NetworkServerClientSendQueueEntry{controller_id, true, reply_hdr, reply_data});
client_info->client_send_coalesce[controller_id] = std::prev(client_info->client_send_queue.end());
}
}
else
{
/*---------------------------------*\
| Non-coalescable event. Backstop |
| a stalled client by dropping |
| the oldest at the cap. |
\*---------------------------------*/
if(client_info->client_send_queue.size() >= NETWORKSERVER_SIGNAL_QUEUE_MAX)
{
NetworkServerClientSendQueueEntry& oldest = client_info->client_send_queue.front();
if(oldest.coalescable)
{
client_info->client_send_coalesce.erase(oldest.controller_id);
}
delete[] oldest.data;
client_info->client_send_queue.pop_front();
LOG_WARNING("[%s] SignalUpdate queue full for client %s, dropping oldest packet", NETWORKSERVER, client_info->client_ip.c_str());
}
client_info->client_send_queue.push_back(NetworkServerClientSendQueueEntry{controller_id, false, reply_hdr, reply_data});
}
}
client_info->client_send_cv.notify_one();
/*-----------------------------------------*\
| Delete data |
\*-----------------------------------------*/
delete[] reply_data;
}
}
ServerClientsMutex.unlock();
}
}
/*---------------------------------------------------------*\
| Per-client SignalUpdate send thread. The blocking send |
| happens here, not inside SignalUpdate, so a slow client |
| blocks only its own queue and thread. The callback drain |
| is never blocked, so a rescan cannot deadlock on a send. |
\*---------------------------------------------------------*/
void NetworkServer::ClientSendThreadFunction(NetworkClientInfo* client_info)
{
while(true)
{
NetworkServerClientSendQueueEntry entry;
{
std::unique_lock<std::mutex> queue_lock(client_info->client_send_mutex);
client_info->client_send_cv.wait(queue_lock, [client_info]{ return !client_info->client_send_queue.empty() || !client_info->client_send_running; });
if(!client_info->client_send_running)
{
break;
}
entry = client_info->client_send_queue.front();
if(entry.coalescable)
{
client_info->client_send_coalesce.erase(entry.controller_id);
}
client_info->client_send_queue.pop_front();
}
/*-------------------------------------------------*\
| send_in_progress still serializes against |
| response sends to this client. Routing those |
| through a per-client mutex is the follow-up. |
\*-------------------------------------------------*/
send_in_progress.lock();
send(client_info->client_sock, (const char *)&entry.header, sizeof(NetPacketHeader), MSG_NOSIGNAL);
send(client_info->client_sock, (const char *)entry.data, entry.header.pkt_size, MSG_NOSIGNAL);
send_in_progress.unlock();
/*-------------------------------------------------*\
| The entry owns the data pointer; free it now that |
| the send is done. |
\*-------------------------------------------------*/
delete[] entry.data;
}
}
-34
View File
@@ -14,12 +14,9 @@
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <list>
#include <map>
#include <mutex>
#include <thread>
#include <queue>
#include <vector>
#include "LogManager.h"
#include "RGBController.h"
#include "NetworkProtocol.h"
@@ -32,24 +29,8 @@
#define MAXSOCK 32
#define TCP_TIMEOUT_SECONDS 5
/*---------------------------------------------------------*\
| Backstop cap on a client's outbound queue. UPDATELEDS |
| coalesces per controller, so this is only hit by a flood |
| of non-coalescable events to a stalled client; the oldest |
| are dropped rather than grow without bound. |
\*---------------------------------------------------------*/
#define NETWORKSERVER_SIGNAL_QUEUE_MAX 1024
typedef void (*NetServerCallback)(void*);
typedef struct
{
unsigned int controller_id;
bool coalescable;
NetPacketHeader header;
unsigned char* data;
} NetworkServerClientSendQueueEntry;
class NetworkClientInfo
{
public:
@@ -64,20 +45,6 @@ public:
std::string client_ip;
bool client_is_local;
bool client_is_local_client;
/*-----------------------------------------------------*\
| Per-client outbound queue for SignalUpdate packets, |
| drained by a dedicated send thread so a slow client |
| never blocks SignalUpdate (which would deadlock a |
| rescan). UPDATELEDS packets coalesce per controller, |
| keeping the queue bounded to one per controller. |
\*-----------------------------------------------------*/
std::list<NetworkServerClientSendQueueEntry> client_send_queue;
std::map<unsigned int, std::list<NetworkServerClientSendQueueEntry>::iterator> client_send_coalesce;
std::mutex client_send_mutex;
std::condition_variable client_send_cv;
std::thread* client_send_thread;
std::atomic<bool> client_send_running;
};
typedef struct
@@ -253,7 +220,6 @@ private:
void ConnectionThreadFunction(int socket_idx);
void ControllerListenThread(NetworkServerControllerThread* this_thread);
void ListenThreadFunction(NetworkClientInfo* client_info);
void ClientSendThreadFunction(NetworkClientInfo* client_info);
void ProfileManagerListenThread(NetworkServerControllerThread* this_thread);
/*-----------------------------------------------------*\
-87
View File
@@ -10,7 +10,6 @@
\*---------------------------------------------------------*/
#include "OpenRGBPluginAPI.h"
#include "RGBController_Dummy.h"
#include "RGBController_Virtual.h"
OpenRGBPluginAPI::OpenRGBPluginAPI()
@@ -186,11 +185,6 @@ bool OpenRGBPluginAPI::LoadProfile(std::string profile_name)
return(profile_manager->LoadProfile(profile_name));
}
bool OpenRGBPluginAPI::SaveProfileFromPlugin(std::string profile_name, std::string plugin_name, nlohmann::json plugin_data)
{
return(profile_manager->SaveProfileFromPlugin(profile_name, plugin_name, plugin_data));
}
/*---------------------------------------------------------*\
| ResourceManager APIs |
\*---------------------------------------------------------*/
@@ -229,87 +223,6 @@ std::vector<RGBControllerInterface*> OpenRGBPluginAPI::GetRGBControllers()
return(resource_manager->GetRGBControllerInterfaces());
}
/*---------------------------------------------------------*\
| RGBController APIs |
\*---------------------------------------------------------*/
nlohmann::json OpenRGBPluginAPI::GetDeviceDescriptionJSON(RGBControllerInterface* controller)
{
return(RGBController::GetDeviceDescriptionJSON((RGBController*)controller));
}
nlohmann::json OpenRGBPluginAPI::GetLEDDescriptionJSON(led led)
{
return(RGBController::GetLEDDescriptionJSON(led));
}
nlohmann::json OpenRGBPluginAPI::GetMatrixMapDescriptionJSON(matrix_map_type matrix_map)
{
return(RGBController::GetMatrixMapDescriptionJSON(matrix_map));
}
nlohmann::json OpenRGBPluginAPI::GetModeDescriptionJSON(mode mode)
{
return(RGBController::GetModeDescriptionJSON(mode));
}
nlohmann::json OpenRGBPluginAPI::GetSegmentDescriptionJSON(segment segment)
{
return(RGBController::GetSegmentDescriptionJSON(segment));
}
nlohmann::json OpenRGBPluginAPI::GetZoneDescriptionJSON(zone zone)
{
return(RGBController::GetZoneDescriptionJSON(zone));
}
RGBControllerInterface* OpenRGBPluginAPI::SetDeviceDescriptionJSON(nlohmann::json controller_json)
{
RGBController_Dummy* new_controller = new RGBController_Dummy();
RGBController::SetDeviceDescriptionJSON(controller_json, (RGBController*)new_controller);
return(new_controller);
}
led OpenRGBPluginAPI::SetLEDDescriptionJSON(nlohmann::json led_json)
{
return(RGBController::SetLEDDescriptionJSON(led_json));
}
matrix_map_type OpenRGBPluginAPI::SetMatrixMapDescriptionJSON(nlohmann::json matrix_map_json)
{
return(RGBController::SetMatrixMapDescriptionJSON(matrix_map_json));
}
mode OpenRGBPluginAPI::SetModeDescriptionJSON(nlohmann::json mode_json)
{
return(RGBController::SetModeDescriptionJSON(mode_json));
}
segment OpenRGBPluginAPI::SetSegmentDescriptionJSON(nlohmann::json segment_json)
{
return(RGBController::SetSegmentDescriptionJSON(segment_json));
}
zone OpenRGBPluginAPI::SetZoneDescriptionJSON(nlohmann::json zone_json)
{
return(RGBController::SetZoneDescriptionJSON(zone_json));
}
bool OpenRGBPluginAPI::CompareControllers(RGBControllerInterface* controller_1, RGBControllerInterface* controller_2)
{
return(RGBController::CompareControllers((RGBController*)controller_1, (RGBController*)controller_2));
}
std::string OpenRGBPluginAPI::DeviceTypeToString(device_type type)
{
return(RGBController::DeviceTypeToString(type));
}
bool OpenRGBPluginAPI::SetModeValuesFromMode(mode& destination, mode& source)
{
return(RGBController::SetModeValuesFromMode(destination, source));
}
/*---------------------------------------------------------*\
| SettingsManager APIs |
\*---------------------------------------------------------*/
-22
View File
@@ -45,7 +45,6 @@ public:
void ClearActiveProfile();
std::vector<std::string> GetProfileList();
bool LoadProfile(std::string profile_name);
bool SaveProfileFromPlugin(std::string profile_name, std::string plugin_name, nlohmann::json plugin_data);
/*-----------------------------------------------------*\
| ResourceManager APIs |
@@ -58,27 +57,6 @@ public:
void WaitForDetection();
std::vector<RGBControllerInterface*> GetRGBControllers();
/*-----------------------------------------------------*\
| RGBController APIs |
\*-----------------------------------------------------*/
nlohmann::json GetDeviceDescriptionJSON(RGBControllerInterface* controller);
nlohmann::json GetLEDDescriptionJSON(led led);
nlohmann::json GetMatrixMapDescriptionJSON(matrix_map_type matrix_map);
nlohmann::json GetModeDescriptionJSON(mode mode);
nlohmann::json GetSegmentDescriptionJSON(segment segment);
nlohmann::json GetZoneDescriptionJSON(zone zone);
RGBControllerInterface* SetDeviceDescriptionJSON(nlohmann::json controller_json);
led SetLEDDescriptionJSON(nlohmann::json led_json);
matrix_map_type SetMatrixMapDescriptionJSON(nlohmann::json matrix_map_json);
mode SetModeDescriptionJSON(nlohmann::json mode_json);
segment SetSegmentDescriptionJSON(nlohmann::json segment_json);
zone SetZoneDescriptionJSON(nlohmann::json zone_json);
bool CompareControllers(RGBControllerInterface* controller_1, RGBControllerInterface* controller_2);
std::string DeviceTypeToString(device_type type);
bool SetModeValuesFromMode(mode& destination, mode& source);
/*-----------------------------------------------------*\
| SettingsManager APIs |
\*-----------------------------------------------------*/
+21 -43
View File
@@ -77,65 +77,43 @@ public:
/*-----------------------------------------------------*\
| LogManager APIs |
\*-----------------------------------------------------*/
virtual void LogEntry(const char* filename, int line, unsigned int level, const char* fmt, ...) = 0;
virtual void LogEntry(const char* filename, int line, unsigned int level, const char* fmt, ...) = 0;
/*-----------------------------------------------------*\
| PluginManager APIs |
\*-----------------------------------------------------*/
virtual RGBControllerInterface* CreateVirtualRGBController(RGBController_Setup* setup) = 0;
virtual void DeleteVirtualRGBController(RGBControllerInterface* rgb_controller) = 0;
virtual void RegisterVirtualRGBController(RGBControllerInterface* rgb_controller) = 0;
virtual void RegisterVirtualRGBControllerInThread(RGBControllerInterface* rgb_controller) = 0;
virtual void UnregisterVirtualRGBController(RGBControllerInterface* rgb_controller) = 0;
virtual void UpdateVirtualRGBController(RGBControllerInterface* rgb_controller, RGBController_Setup* setup) = 0;
virtual void UnregisterVirtualRGBControllerInThread(RGBControllerInterface* rgb_controller) = 0;
virtual RGBControllerInterface* CreateVirtualRGBController(RGBController_Setup* setup) = 0;
virtual void DeleteVirtualRGBController(RGBControllerInterface* rgb_controller) = 0;
virtual void RegisterVirtualRGBController(RGBControllerInterface* rgb_controller) = 0;
virtual void RegisterVirtualRGBControllerInThread(RGBControllerInterface* rgb_controller) = 0;
virtual void UnregisterVirtualRGBController(RGBControllerInterface* rgb_controller) = 0;
virtual void UpdateVirtualRGBController(RGBControllerInterface* rgb_controller, RGBController_Setup* setup) = 0;
virtual void UnregisterVirtualRGBControllerInThread(RGBControllerInterface* rgb_controller) = 0;
/*-----------------------------------------------------*\
| ProfileManager APIs |
\*-----------------------------------------------------*/
virtual void ClearActiveProfile() = 0;
virtual std::vector<std::string> GetProfileList() = 0;
virtual bool LoadProfile(std::string profile_name) = 0;
virtual bool SaveProfileFromPlugin(std::string profile_name, std::string plugin_name, nlohmann::json plugin_data) = 0;
virtual void ClearActiveProfile() = 0;
virtual std::vector<std::string> GetProfileList() = 0;
virtual bool LoadProfile(std::string profile_name) = 0;
/*-----------------------------------------------------*\
| ResourceManager APIs |
\*-----------------------------------------------------*/
virtual filesystem::path GetConfigurationDirectory() = 0;
virtual bool GetDetectionEnabled() = 0;
virtual unsigned int GetDetectionPercent() = 0;
virtual std::string GetDetectionString() = 0;
virtual void RescanDevices() = 0;
virtual void WaitForDetection() = 0;
virtual std::vector<RGBControllerInterface*> GetRGBControllers() = 0;
/*-----------------------------------------------------*\
| RGBController APIs |
\*-----------------------------------------------------*/
virtual nlohmann::json GetDeviceDescriptionJSON(RGBControllerInterface* controller) = 0;
virtual nlohmann::json GetLEDDescriptionJSON(led led) = 0;
virtual nlohmann::json GetMatrixMapDescriptionJSON(matrix_map_type matrix_map) = 0;
virtual nlohmann::json GetModeDescriptionJSON(mode mode) = 0;
virtual nlohmann::json GetSegmentDescriptionJSON(segment segment) = 0;
virtual nlohmann::json GetZoneDescriptionJSON(zone zone) = 0;
virtual RGBControllerInterface* SetDeviceDescriptionJSON(nlohmann::json controller_json) = 0;
virtual led SetLEDDescriptionJSON(nlohmann::json led_json) = 0;
virtual matrix_map_type SetMatrixMapDescriptionJSON(nlohmann::json matrix_map_json) = 0;
virtual mode SetModeDescriptionJSON(nlohmann::json mode_json) = 0;
virtual segment SetSegmentDescriptionJSON(nlohmann::json segment_json) = 0;
virtual zone SetZoneDescriptionJSON(nlohmann::json zone_json) = 0;
virtual bool CompareControllers(RGBControllerInterface* controller_1, RGBControllerInterface* controller_2) = 0;
virtual std::string DeviceTypeToString(device_type type) = 0;
virtual bool SetModeValuesFromMode(mode& destination, mode& source) = 0;
virtual filesystem::path GetConfigurationDirectory() = 0;
virtual bool GetDetectionEnabled() = 0;
virtual unsigned int GetDetectionPercent() = 0;
virtual std::string GetDetectionString() = 0;
virtual void RescanDevices() = 0;
virtual void WaitForDetection() = 0;
virtual std::vector<RGBControllerInterface*> GetRGBControllers() = 0;
/*-----------------------------------------------------*\
| SettingsManager APIs |
\*-----------------------------------------------------*/
virtual nlohmann::json GetSettings(std::string settings_key) = 0;
virtual void SaveSettings() = 0;
virtual void SetSettings(std::string settings_key, nlohmann::json new_settings) = 0;
virtual nlohmann::json GetSettings(std::string settings_key) = 0;
virtual void SaveSettings() = 0;
virtual void SetSettings(std::string settings_key, nlohmann::json new_settings) = 0;
};
class OpenRGBPluginInterface
+20 -25
View File
@@ -170,7 +170,6 @@ void PluginManager::AddPlugin(const filesystem::path& path, bool is_system)
/*-----------------------------------------------------*\
| Prepare variables and open plugin settings |
\*-----------------------------------------------------*/
std::string path_string = path.generic_u8string();
OpenRGBPluginInterface* plugin = nullptr;
unsigned int plugin_idx;
json plugin_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Plugins");
@@ -184,7 +183,7 @@ void PluginManager::AddPlugin(const filesystem::path& path, bool is_system)
{
LOG_WARNING("[%s] Checking remove %d, %s", PLUGINMANAGER, plugin_remove_idx, to_string(plugin_settings["plugins_remove"][plugin_remove_idx]).c_str());
if(plugin_settings["plugins_remove"][plugin_remove_idx] == path_string)
if(plugin_settings["plugins_remove"][plugin_remove_idx] == path.generic_u8string())
{
/*-----------------------------------------*\
| Delete the plugin file |
@@ -210,7 +209,7 @@ void PluginManager::AddPlugin(const filesystem::path& path, bool is_system)
{
if(path == ActivePlugins[plugin_idx].path)
{
LOG_WARNING("[%s] Plugin file %s already in list, skipping.", PLUGINMANAGER, path_string.c_str());
LOG_WARNING("[%s] Plugin file %s already in list, skipping.", PLUGINMANAGER, path.c_str());
return;
}
}
@@ -218,6 +217,7 @@ void PluginManager::AddPlugin(const filesystem::path& path, bool is_system)
/*-----------------------------------------------------*\
| Create a QPluginLoader and extract the metadata |
\*-----------------------------------------------------*/
std::string path_string = path.generic_u8string();
QPluginLoader* loader = new QPluginLoader(QString::fromStdString(path_string));
QJsonObject metadata = loader->metaData();
unsigned int plugin_api_version = 0;
@@ -233,7 +233,7 @@ void PluginManager::AddPlugin(const filesystem::path& path, bool is_system)
}
else
{
LOG_WARNING("[%s] Plugin %s does not have a MetaData field, skipping.", PLUGINMANAGER, path_string.c_str());
LOG_WARNING("[%s] Plugin %s does not have a MetaData field, skipping.", PLUGINMANAGER, path.c_str());
return;
}
@@ -243,7 +243,7 @@ void PluginManager::AddPlugin(const filesystem::path& path, bool is_system)
}
else
{
LOG_WARNING("[%s] Plugin %s does not have an OpenRGBPluginAPIVersion field, skipping.", PLUGINMANAGER, path_string.c_str());
LOG_WARNING("[%s] Plugin %s does not have an OpenRGBPluginAPIVersion field, skipping.", PLUGINMANAGER, path.c_str());
return;
}
@@ -253,7 +253,7 @@ void PluginManager::AddPlugin(const filesystem::path& path, bool is_system)
}
else
{
LOG_WARNING("[%s] Plugin %s does not have an Id field, skipping.", PLUGINMANAGER, path_string.c_str());
LOG_WARNING("[%s] Plugin %s does not have an Id field, skipping.", PLUGINMANAGER, path.c_str());
return;
}
@@ -291,29 +291,19 @@ void PluginManager::AddPlugin(const filesystem::path& path, bool is_system)
entry.info.URL = plugin_url.toStdString();
entry.info.Version = plugin_version.toStdString();
entry.id = plugin_id;
entry.incompatible = true;
entry.incompatible = (plugin_api_version == OPENRGB_PLUGIN_API_VERSION);
entry.is_system = is_system;
entry.loader = loader;
entry.path = path_string;
entry.plugin = plugin;
entry.widget = nullptr;
/*-----------------------------------------------------*\
| If metadata indicates a mismatching API version, |
| skip loading |
\*-----------------------------------------------------*/
if(plugin_api_version != OPENRGB_PLUGIN_API_VERSION)
{
LOG_WARNING("[%s] Plugin %s has an incompatible API version", PLUGINMANAGER, path_string.c_str());
goto add_plugin_entry;
}
/*-----------------------------------------------------*\
| Check to see if this plugin's ID already exists |
\*-----------------------------------------------------*/
for(plugin_idx = 0; plugin_idx < ActivePlugins.size(); plugin_idx++)
{
if(plugin_id == ActivePlugins[plugin_idx].id && !ActivePlugins[plugin_idx].incompatible)
if(plugin_id == ActivePlugins[plugin_idx].id)
{
entry.info.Description = "This plugin has a duplicate ID as an already loaded plugin and cannot be loaded.";
LOG_WARNING("[%s] Plugin ID %s already in list.", PLUGINMANAGER, plugin_id.toStdString().c_str());
@@ -321,18 +311,23 @@ void PluginManager::AddPlugin(const filesystem::path& path, bool is_system)
}
}
/*-----------------------------------------------------*\
| If metadata indicates a matching API version, |
| continue loading |
\*-----------------------------------------------------*/
if(plugin_api_version == OPENRGB_PLUGIN_API_VERSION)
{
QObject* instance = loader->instance();
if(!loader->isLoaded())
{
LOG_WARNING("[%s] Plugin %s cannot be loaded: %s", PLUGINMANAGER, path_string.c_str(), loader->errorString().toStdString().c_str());
LOG_WARNING("[%s] Plugin %s cannot be loaded: %s", PLUGINMANAGER, path.c_str(), loader->errorString().toStdString().c_str());
goto add_plugin_entry;
}
if(!instance)
{
LOG_WARNING("[%s] Plugin %s cannot be instantiated.", PLUGINMANAGER, path_string.c_str());
LOG_WARNING("[%s] Plugin %s cannot be instantiated.", PLUGINMANAGER, path.c_str());
goto add_plugin_entry;
}
@@ -343,13 +338,13 @@ void PluginManager::AddPlugin(const filesystem::path& path, bool is_system)
if(!plugin)
{
LOG_WARNING("[%s] Plugin %s cannot be casted to OpenRGBPluginInterface", PLUGINMANAGER, path_string.c_str());
LOG_WARNING("[%s] Plugin %s cannot be casted to OpenRGBPluginInterface", PLUGINMANAGER, path.c_str());
goto add_plugin_entry;
}
if(plugin->GetPluginAPIVersion() != OPENRGB_PLUGIN_API_VERSION)
{
LOG_WARNING("[%s] Plugin %s has an incompatible API version", PLUGINMANAGER, path_string.c_str());
LOG_WARNING("[%s] Plugin %s has a compatible API version", PLUGINMANAGER, path.c_str());
goto add_plugin_entry;
}
@@ -438,7 +433,7 @@ void PluginManager::RemovePlugin(const filesystem::path& path)
{
unsigned int plugin_idx;
LOG_TRACE("[%s] Attempting to remove plugin %s", PLUGINMANAGER, path.generic_u8string().c_str());
LOG_TRACE("[%s] Attempting to remove plugin %s", PLUGINMANAGER, path.c_str());
/*-----------------------------------------------------*\
| Search active plugins to see if this path already |
@@ -458,7 +453,7 @@ void PluginManager::RemovePlugin(const filesystem::path& path)
\*-----------------------------------------------------*/
if(plugin_idx == ActivePlugins.size())
{
LOG_TRACE("[%s] Plugin %s not active", PLUGINMANAGER, path.generic_u8string().c_str());
LOG_TRACE("[%s] Plugin %s not active", PLUGINMANAGER, path.c_str());
return;
}
@@ -468,7 +463,7 @@ void PluginManager::RemovePlugin(const filesystem::path& path)
\*-----------------------------------------------------*/
if(ActivePlugins[plugin_idx].loader->isLoaded())
{
LOG_TRACE("[%s] Plugin %s is active, unloading", PLUGINMANAGER, path.generic_u8string().c_str());
LOG_TRACE("[%s] Plugin %s is active, unloading", PLUGINMANAGER, path.c_str());
UnloadPlugin(&ActivePlugins[plugin_idx]);
}
+1 -224
View File
@@ -176,106 +176,6 @@ std::string ProfileManager::GetActiveProfile()
return(active_profile);
}
std::vector<RGBController*> ProfileManager::GetControllerListFromLegacyProfile(std::string profile_name, bool sizes)
{
unsigned int controller_size;
unsigned int profile_offset = 0;
std::vector<RGBController*> temp_controllers;
filesystem::path filename = configuration_directory / StringUtils::make_filename(profile_name);
/*-----------------------------------------------------*\
| Determine file extension |
\*-----------------------------------------------------*/
if(sizes)
{
filename.concat(".ors");
}
else
{
if(filename.extension() != ".orp")
{
filename.concat(".orp");
}
}
/*-----------------------------------------------------*\
| Open input file in binary mode |
\*-----------------------------------------------------*/
std::ifstream profile_file(filename, std::ios::in | std::ios::binary);
if(!profile_file.is_open())
{
LOG_WARNING("[%s] Unable to open legacy profile: %s", PROFILEMANAGER, filename.string().c_str());
return(temp_controllers);
}
/*-----------------------------------------------------*\
| Read and verify file header |
\*-----------------------------------------------------*/
char profile_string[16] = "";
unsigned int profile_version = 0;
profile_file.read(profile_string, 16);
profile_file.read((char *)&profile_version, sizeof(unsigned int));
/*-----------------------------------------------------*\
| Profile version started at 1 and protocol version |
| started at 0. Version 1 profiles should use protocol |
| 0, but 2 or greater should be synchronized |
\*-----------------------------------------------------*/
if(profile_version == 1)
{
profile_version = 0;
}
profile_offset += 16 + sizeof(unsigned int);
profile_file.seekg(profile_offset);
if(strcmp(profile_string, OPENRGB_PROFILE_HEADER) == 0)
{
if(profile_version <= OPENRGB_PROFILE_VERSION)
{
/*---------------------------------------------*\
| Read controller data from file until EOF |
\*---------------------------------------------*/
while(!(profile_file.peek() == EOF))
{
profile_file.read((char *)&controller_size, sizeof(controller_size));
unsigned char *controller_data = new unsigned char[controller_size];
profile_file.seekg(profile_offset);
profile_file.read((char *)controller_data, controller_size);
RGBController_Dummy *temp_controller = new RGBController_Dummy();
RGBController::SetDeviceDescription(controller_data + sizeof(unsigned int), controller_size - sizeof(unsigned int), temp_controller, profile_version);
temp_controllers.push_back(temp_controller);
delete[] controller_data;
profile_offset += controller_size;
profile_file.seekg(profile_offset);
}
}
else
{
LOG_WARNING("[%s] Legacy profile has unsupported version %u: %s", PROFILEMANAGER, profile_version, filename.string().c_str());
return(temp_controllers);
}
}
else
{
LOG_WARNING("[%s] Unable to read legacy profile: %s", PROFILEMANAGER, filename.string().c_str());
return(temp_controllers);
}
return(temp_controllers);
}
std::vector<RGBController*> ProfileManager::GetControllerListFromProfileJson(nlohmann::json profile_json)
{
std::vector<RGBController*> profile_controllers;
@@ -781,44 +681,7 @@ bool ProfileManager::SaveProfileFromJSON(nlohmann::json profile_json)
\*-------------------------------------------------*/
UpdateProfileList();
return(true);
}
else
{
return(false);
}
}
bool ProfileManager::SaveProfileFromPlugin(std::string profile_name, std::string plugin_name, nlohmann::json plugin_data)
{
/*-----------------------------------------------------*\
| If a name was entered, save the profile file |
\*-----------------------------------------------------*/
if(profile_name != "")
{
/*-------------------------------------------------*\
| Start filling in profile json data |
\*-------------------------------------------------*/
nlohmann::json profile_json;
profile_json["profile_version"] = OPENRGB_PROFILE_VERSION;
profile_json["profile_name"] = profile_name;
profile_json["plugins"][plugin_name] = plugin_data;
if(ResourceManager::get()->IsLocalClient() && (ResourceManager::get()->GetLocalClient()->GetSupportsProfileManagerAPI()))
{
/*---------------------------------------------*\
| Upload the profile to the server |
\*---------------------------------------------*/
ResourceManager::get()->GetLocalClient()->ProfileManager_UploadProfile(profile_json.dump());
}
else
{
/*---------------------------------------------*\
| Save the profile to file from the JSON |
\*---------------------------------------------*/
SaveProfileFromJSON(profile_json);
}
SetActiveProfile(profile_json["profile_name"]);
return(true);
}
@@ -969,92 +832,6 @@ void ProfileManager::SetConfigurationDirectory(const filesystem::path& directory
manually_configured_rgb_controllers = GetControllerListFromSavedConfiguration();
}
void ProfileManager::MigrateLegacyProfiles()
{
/*-----------------------------------------------------*\
| Migrate any controller configuration in sizes.ors |
| that doesn't already exist in the manual |
| configuration list |
\*-----------------------------------------------------*/
std::vector<RGBController*> sizes_controllers;
sizes_controllers = GetControllerListFromLegacyProfile("sizes", true);
if(sizes_controllers.size() > 0)
{
for(std::size_t controller_idx = 0; controller_idx < sizes_controllers.size(); controller_idx++)
{
bool found = false;
for(std::size_t manually_configured_idx = 0; manually_configured_idx < manually_configured_rgb_controllers.size(); manually_configured_idx++)
{
if(RGBController::CompareControllers(sizes_controllers[controller_idx], manually_configured_rgb_controllers[manually_configured_idx]))
{
found = true;
break;
}
}
if(!found)
{
manually_configured_rgb_controllers.push_back(sizes_controllers[controller_idx]);
}
}
SaveConfiguration();
}
/*-----------------------------------------------------*\
| Look at each file in the configuration directory for |
| files with .orp extension |
\*-----------------------------------------------------*/
for(const filesystem::directory_entry &entry : filesystem::directory_iterator(configuration_directory))
{
std::string filename = entry.path().filename().string();
if(filename.size() <= 4 || filename.substr(filename.size() - 4) != ".orp")
{
continue;
}
/*-------------------------------------------------*\
| Determine the profile name based on the filename |
\*-------------------------------------------------*/
std::string profile_name = StringUtils::make_filename(filename.substr(0, filename.size() - 4));
std::vector<RGBController*> profile_controllers;
std::vector<std::string> profile_plugin_data;
/*-------------------------------------------------*\
| If this profile name already exists, skip it |
\*-------------------------------------------------*/
bool found = false;
for(std::size_t profile_idx = 0; profile_idx < profile_list.size(); profile_idx++)
{
if(profile_name == profile_list[profile_idx])
{
found = true;
break;
}
}
if(found)
{
continue;
}
/*-------------------------------------------------*\
| Read the controller data from the profile |
\*-------------------------------------------------*/
profile_controllers = GetControllerListFromLegacyProfile(profile_name, false);
/*-------------------------------------------------*\
| Save the profile in JSON format |
\*-------------------------------------------------*/
SaveProfileCustom(profile_name, profile_controllers, 0, false, profile_plugin_data);
}
}
void ProfileManager::SetProfileListFromDescription(unsigned int /*data_size*/, char * data_buf)
{
unsigned int data_ptr = sizeof(unsigned int);
-4
View File
@@ -83,7 +83,6 @@ public:
void DeleteProfile(std::string profile_name);
std::string GetActiveProfile();
std::vector<RGBController*> GetControllerListFromLegacyProfile(std::string profile_name, bool sizes);
std::vector<RGBController*> GetControllerListFromProfileJson(nlohmann::json profile_json);
std::vector<RGBController*> GetControllerListFromProfileName(std::string profile_name);
std::vector<RGBController*> GetControllerListFromSavedConfiguration();
@@ -111,8 +110,6 @@ public:
void OnProfileAboutToLoad();
void OnProfileLoaded(std::string profile_json_string);
void MigrateLegacyProfiles();
/*-----------------------------------------------------*\
| Callback Registration Functions |
\*-----------------------------------------------------*/
@@ -125,7 +122,6 @@ public:
bool SaveProfile(std::string profile_name);
bool SaveProfileCustom(std::string profile_name, std::vector<RGBController*> controllers, RGBColor base_color, bool base_color_enabled, std::vector<std::string> enabled_plugins);
bool SaveProfileFromJSON(nlohmann::json profile_json);
bool SaveProfileFromPlugin(std::string profile_name, std::string plugin_name, nlohmann::json plugin_data);
bool SaveConfiguration();
void SetActiveProfile(std::string profile_name);
+46 -61
View File
@@ -1075,8 +1075,7 @@ void RGBController::SetZoneModeDirection(unsigned int zone, unsigned int mode, u
&& (mode < zones[zone].modes.size())
&& (((zones[zone].modes[mode].flags & MODE_FLAG_HAS_DIRECTION_HV) && ((direction == MODE_DIRECTION_HORIZONTAL) || (direction == MODE_DIRECTION_VERTICAL)))
|| ((zones[zone].modes[mode].flags & MODE_FLAG_HAS_DIRECTION_LR) && ((direction == MODE_DIRECTION_LEFT) || (direction == MODE_DIRECTION_RIGHT)))
|| ((zones[zone].modes[mode].flags & MODE_FLAG_HAS_DIRECTION_UD) && ((direction == MODE_DIRECTION_UP) || (direction == MODE_DIRECTION_DOWN)))
|| ((zones[zone].modes[mode].flags & MODE_FLAG_HAS_DIRECTION_DIAG) && (direction >= MODE_DIRECTION_UP_LEFT) && (direction <= MODE_DIRECTION_DOWN_RIGHT))))
|| ((zones[zone].modes[mode].flags & MODE_FLAG_HAS_DIRECTION_UD) && ((direction == MODE_DIRECTION_UP) || (direction == MODE_DIRECTION_DOWN)))))
{
zones[zone].modes[mode].direction = direction;
}
@@ -1409,8 +1408,7 @@ void RGBController::SetModeDirection(unsigned int mode, unsigned int direction)
if((mode < modes.size())
&& (((modes[mode].flags & MODE_FLAG_HAS_DIRECTION_HV) && ((direction == MODE_DIRECTION_HORIZONTAL) || (direction == MODE_DIRECTION_VERTICAL)))
|| ((modes[mode].flags & MODE_FLAG_HAS_DIRECTION_LR) && ((direction == MODE_DIRECTION_LEFT) || (direction == MODE_DIRECTION_RIGHT)))
|| ((modes[mode].flags & MODE_FLAG_HAS_DIRECTION_UD) && ((direction == MODE_DIRECTION_UP) || (direction == MODE_DIRECTION_DOWN)))
|| ((modes[mode].flags & MODE_FLAG_HAS_DIRECTION_DIAG) && (direction >= MODE_DIRECTION_UP_LEFT) && (direction <= MODE_DIRECTION_DOWN_RIGHT))))
|| ((modes[mode].flags & MODE_FLAG_HAS_DIRECTION_UD) && ((direction == MODE_DIRECTION_UP) || (direction == MODE_DIRECTION_DOWN)))))
{
modes[mode].direction = direction;
}
@@ -1859,17 +1857,17 @@ void RGBController::UnregisterUpdateCallback(void * callback_arg)
| callback, return immediately rather than waiting for |
| callback completion to avoid deadlock. |
\*-----------------------------------------------------*/
//if(SignalCalls != 0)
//{
// return;
//}
if(SignalCalls != 0)
{
return;
}
/*-----------------------------------------------------*\
| Otherwise, wait for all currently active callbacks to |
| complete before returning |
\*-----------------------------------------------------*/
//std::unique_lock<std::mutex> wait_lock(SignalMutex);
//SignalCallsDone.wait(wait_lock, [this]{ return SignalCalls == 0; });
std::unique_lock<std::mutex> wait_lock(SignalMutex);
SignalCallsDone.wait(wait_lock, [this]{ return SignalCalls == 0; });
}
void RGBController::ClearCallbacks()
@@ -1884,17 +1882,17 @@ void RGBController::ClearCallbacks()
| callback, return immediately rather than waiting for |
| callback completion to avoid deadlock. |
\*-----------------------------------------------------*/
//if(SignalCalls != 0)
//{
// return;
//}
if(SignalCalls != 0)
{
return;
}
/*-----------------------------------------------------*\
| Otherwise, wait for all currently active callbacks to |
| complete before returning |
\*-----------------------------------------------------*/
//std::unique_lock<std::mutex> wait_lock(SignalMutex);
//SignalCallsDone.wait(wait_lock, [this]{ return SignalCalls == 0; });
std::unique_lock<std::mutex> wait_lock(SignalMutex);
SignalCallsDone.wait(wait_lock, [this]{ return SignalCalls == 0; });
}
void RGBController::SignalUpdate(unsigned int update_reason)
@@ -1915,14 +1913,14 @@ void RGBController::SignalUpdate(unsigned int update_reason)
| count. Return immediately if the controller has been |
| shut down. |
\*-----------------------------------------------------*/
//SignalMutex.lock();
//if(SignalShutdown)
//{
// UpdateMutex.unlock();
// return;
//}
//SignalCalls++;
//SignalMutex.unlock();
SignalMutex.lock();
if(SignalShutdown)
{
UpdateMutex.unlock();
return;
}
SignalCalls++;
SignalMutex.unlock();
/*-----------------------------------------------------*\
| Copy the list of callbacks and unlock the update |
@@ -1931,7 +1929,7 @@ void RGBController::SignalUpdate(unsigned int update_reason)
callbacks = UpdateCallbacks;
callback_args = UpdateCallbackArgs;
//UpdateMutex.unlock();
UpdateMutex.unlock();
/*-----------------------------------------------------*\
| Invoke the copied callbacks |
@@ -1941,21 +1939,20 @@ void RGBController::SignalUpdate(unsigned int update_reason)
callbacks[callback_idx](callback_args[callback_idx], update_reason, this);
}
UpdateMutex.unlock();
/*-----------------------------------------------------*\
| Decrement the signal call count once callbacks have |
| been called. Notify anyone waiting on signal calls |
| to be done. |
\*-----------------------------------------------------*/
//SignalMutex.lock();
//SignalCalls--;
SignalMutex.lock();
SignalCalls--;
//if(SignalCalls == 0)
//{
// SignalCallsDone.notify_all();
//}
if(SignalCalls == 0)
{
SignalCallsDone.notify_all();
}
//SignalMutex.unlock();
SignalMutex.unlock();
}
/*---------------------------------------------------------*\
@@ -2885,15 +2882,9 @@ unsigned char * RGBController::GetModeDescriptionData(unsigned char* data_ptr, m
/*-----------------------------------------------------*\
| Copy in mode flags (data) |
| Strip diagonal flag for SDK v5 and earlier clients |
\*-----------------------------------------------------*/
unsigned int flags = mode.flags;
if(protocol_version < 6)
{
flags &= ~MODE_FLAG_HAS_DIRECTION_DIAG;
}
memcpy(data_ptr, &flags, sizeof(flags));
data_ptr += sizeof(flags);
memcpy(data_ptr, &mode.flags, sizeof(mode.flags));
data_ptr += sizeof(mode.flags);
/*-----------------------------------------------------*\
| Copy in mode speed_min (data) |
@@ -2950,15 +2941,9 @@ unsigned char * RGBController::GetModeDescriptionData(unsigned char* data_ptr, m
/*-----------------------------------------------------*\
| Copy in mode direction (data) |
| Clamp diagonal directions for SDK v5 and earlier |
\*-----------------------------------------------------*/
unsigned int dir = mode.direction;
if(protocol_version < 6 && dir > MODE_DIRECTION_VERTICAL)
{
dir = MODE_DIRECTION_LEFT;
}
memcpy(data_ptr, &dir, sizeof(dir));
data_ptr += sizeof(dir);
memcpy(data_ptr, &mode.direction, sizeof(mode.direction));
data_ptr += sizeof(mode.direction);
/*-----------------------------------------------------*\
| Copy in mode color_mode (data) |
@@ -3189,12 +3174,6 @@ unsigned char * RGBController::GetZoneDescriptionData(unsigned char* data_ptr, z
\*-----------------------------------------------------*/
if(protocol_version >= 6)
{
/*-------------------------------------------------*\
| Copy in active mode |
\*-------------------------------------------------*/
memcpy(data_ptr, &zone.active_mode, sizeof(zone.active_mode));
data_ptr += sizeof(zone.active_mode);
/*-------------------------------------------------*\
| Copy in number of modes |
\*-------------------------------------------------*/
@@ -3203,6 +3182,12 @@ unsigned char * RGBController::GetZoneDescriptionData(unsigned char* data_ptr, z
memcpy(data_ptr, &num_modes, sizeof(num_modes));
data_ptr += sizeof(num_modes);
/*-------------------------------------------------*\
| Copy in active mode |
\*-------------------------------------------------*/
memcpy(data_ptr, &zone.active_mode, sizeof(zone.active_mode));
data_ptr += sizeof(zone.active_mode);
/*-------------------------------------------------*\
| Copy in modes |
\*-------------------------------------------------*/
@@ -3276,8 +3261,8 @@ unsigned int RGBController::GetZoneDescriptionSize(zone zone, unsigned int proto
{
unsigned short num_modes = (unsigned short)zone.modes.size();
data_size += sizeof(zone.active_mode);
data_size += sizeof(num_modes);
data_size += sizeof(zone.active_mode);
for(int mode_index = 0; mode_index < num_modes; mode_index++)
{
@@ -3904,17 +3889,17 @@ unsigned char* RGBController::SetZoneDescription(unsigned char* data_ptr, unsign
\*-----------------------------------------------------*/
if(protocol_version >= 6)
{
/*-------------------------------------------------*\
| Copy in active mode |
\*-------------------------------------------------*/
COPY_DATA_FIELD(data_ptr, data_start, zone->active_mode);
/*-------------------------------------------------*\
| Copy in number of modes |
\*-------------------------------------------------*/
unsigned short num_modes;
COPY_DATA_FIELD(data_ptr, data_start, num_modes);
/*-------------------------------------------------*\
| Copy in active mode |
\*-------------------------------------------------*/
COPY_DATA_FIELD(data_ptr, data_start, zone->active_mode);
/*-------------------------------------------------*\
| Copy in modes |
\*-------------------------------------------------*/
-5
View File
@@ -73,7 +73,6 @@ enum
MODE_FLAG_AUTOMATIC_SAVE = (1 << 9), /* Mode automatically saves */
MODE_FLAG_REQUIRES_ENTIRE_DEVICE = (1 << 10),/* Mode always applies to entire */
/* device, overrides per-zone modes */
MODE_FLAG_HAS_DIRECTION_DIAG = (1 << 11),/* Mode has diagonal direction */
};
/*---------------------------------------------------------*\
@@ -87,10 +86,6 @@ enum
MODE_DIRECTION_DOWN = 3, /* Mode direction down */
MODE_DIRECTION_HORIZONTAL = 4, /* Mode direction horizontal */
MODE_DIRECTION_VERTICAL = 5, /* Mode direction vertical */
MODE_DIRECTION_UP_LEFT = 6, /* Mode direction up-left */
MODE_DIRECTION_UP_RIGHT = 7, /* Mode direction up-right */
MODE_DIRECTION_DOWN_LEFT = 8, /* Mode direction down-left */
MODE_DIRECTION_DOWN_RIGHT = 9, /* Mode direction down-right */
};
/*---------------------------------------------------------*\
-18
View File
@@ -99,9 +99,7 @@ void RGBController_Virtual::DeviceConfigureZone(int zone_idx)
{
if(DeviceConfigureZone_ptr)
{
AccessMutex.unlock();
DeviceConfigureZone_ptr(object_ptr, zone_idx);
AccessMutex.lock();
}
}
@@ -109,9 +107,7 @@ void RGBController_Virtual::DeviceUpdateLEDs()
{
if(DeviceUpdateLEDs_ptr)
{
AccessMutex.unlock_shared();
DeviceUpdateLEDs_ptr(object_ptr);
AccessMutex.lock_shared();
}
}
@@ -119,9 +115,7 @@ void RGBController_Virtual::DeviceUpdateZoneLEDs(int zone)
{
if(DeviceUpdateZoneLEDs_ptr)
{
AccessMutex.unlock_shared();
DeviceUpdateZoneLEDs_ptr(object_ptr, zone);
AccessMutex.lock_shared();
}
}
@@ -129,9 +123,7 @@ void RGBController_Virtual::DeviceUpdateSingleLED(int led)
{
if(DeviceUpdateSingleLED_ptr)
{
AccessMutex.unlock_shared();
DeviceUpdateSingleLED_ptr(object_ptr, led);
AccessMutex.lock_shared();
}
}
@@ -139,9 +131,7 @@ void RGBController_Virtual::DeviceUpdateMode()
{
if(DeviceUpdateMode_ptr)
{
AccessMutex.unlock_shared();
DeviceUpdateMode_ptr(object_ptr);
AccessMutex.lock_shared();
}
}
@@ -149,9 +139,7 @@ void RGBController_Virtual::DeviceUpdateZoneMode(int zone)
{
if(DeviceUpdateZoneMode_ptr)
{
AccessMutex.unlock_shared();
DeviceUpdateZoneMode_ptr(object_ptr, zone);
AccessMutex.lock_shared();
}
}
@@ -159,9 +147,7 @@ void RGBController_Virtual::DeviceSaveMode()
{
if(DeviceSaveMode_ptr)
{
AccessMutex.unlock_shared();
DeviceSaveMode_ptr(object_ptr);
AccessMutex.lock_shared();
}
}
@@ -169,9 +155,7 @@ void RGBController_Virtual::DeviceUpdateDeviceSpecificConfiguration()
{
if(DeviceUpdateDeviceSpecificConfiguration_ptr)
{
AccessMutex.unlock();
DeviceUpdateDeviceSpecificConfiguration_ptr(object_ptr);
AccessMutex.lock();
}
}
@@ -179,8 +163,6 @@ void RGBController_Virtual::DeviceUpdateDeviceSpecificZoneConfiguration(int zone
{
if(DeviceUpdateDeviceSpecificZoneConfiguration_ptr)
{
AccessMutex.unlock();
DeviceUpdateDeviceSpecificZoneConfiguration_ptr(object_ptr, zone);
AccessMutex.lock();
}
}
+22 -184
View File
@@ -86,19 +86,6 @@ static void ResourceManagerNetworkClientCallback(void* this_ptr, unsigned int up
this_obj->UpdateDeviceList();
break;
case NETWORKCLIENT_UPDATE_REASON_CLIENT_CONNECTED:
this_obj->SignalResourceManagerUpdate(RESOURCEMANAGER_UPDATE_REASON_CLIENT_INFO_UPDATED);
break;
/*-------------------------------------------------*\
| Unplanned connection loss: free the client's |
| orphaned controllers on the teardown thread |
\*-------------------------------------------------*/
case NETWORKCLIENT_UPDATE_REASON_CLIENT_DISCONNECTED:
this_obj->SignalResourceManagerUpdate(RESOURCEMANAGER_UPDATE_REASON_CLIENT_INFO_UPDATED);
this_obj->QueueClientTeardown();
break;
case NETWORKCLIENT_UPDATE_REASON_DETECTION_STARTED:
this_obj->SignalResourceManagerUpdate(RESOURCEMANAGER_UPDATE_REASON_DETECTION_STARTED);
break;
@@ -164,13 +151,6 @@ ResourceManager::ResourceManager()
plugin_manager = NULL;
server = NULL;
/*-----------------------------------------------------*\
| Start the client teardown thread |
\*-----------------------------------------------------*/
ClientTeardownPending = false;
ClientTeardownRunning = true;
ClientTeardownThread = new std::thread(&ResourceManager::ClientTeardownThreadFunction, this);
SetupConfigurationDirectory();
/*-----------------------------------------------------*\
@@ -294,21 +274,7 @@ ResourceManager::ResourceManager()
ResourceManager::~ResourceManager()
{
/*-----------------------------------------------------*\
| Stop the client teardown thread |
\*-----------------------------------------------------*/
ClientTeardownMutex.lock();
ClientTeardownRunning = false;
ClientTeardownMutex.unlock();
ClientTeardownCv.notify_all();
if(ClientTeardownThread)
{
ClientTeardownThread->join();
delete ClientTeardownThread;
ClientTeardownThread = nullptr;
}
}
/*---------------------------------------------------------*\
@@ -569,155 +535,38 @@ void ResourceManager::RegisterNetworkClient(NetworkClient* new_client)
{
new_client->RegisterNetworkClientCallback(ResourceManagerNetworkClientCallback, this);
ClientListMutex.lock();
clients.push_back(new_client);
ClientListMutex.unlock();
}
void ResourceManager::UnregisterNetworkClient(NetworkClient* network_client)
{
/*-----------------------------------------------------*\
| Queue the removal for the teardown thread. This is |
| called on the GUI thread, and the teardown rebuilds |
| the device list, whose DEVICE_LIST_UPDATED handler |
| is a BlockingQueuedConnection to the GUI thread. It |
| also keeps StopClient's join off the GUI thread |
| Stop the disconnecting client |
\*-----------------------------------------------------*/
ClientTeardownMutex.lock();
network_client->StopClient();
if(std::find(clients_to_remove.begin(), clients_to_remove.end(), network_client) == clients_to_remove.end())
/*-----------------------------------------------------*\
| Clear callbacks from the client before removal |
\*-----------------------------------------------------*/
network_client->ClearCallbacks();
/*-----------------------------------------------------*\
| Find the client to remove and remove it from the |
| clients list |
\*-----------------------------------------------------*/
std::vector<NetworkClient*>::iterator client_it = std::find(clients.begin(), clients.end(), network_client);
if(client_it != clients.end())
{
clients_to_remove.push_back(network_client);
clients.erase(client_it);
}
ClientTeardownPending = true;
/*-----------------------------------------------------*\
| Delete the client |
\*-----------------------------------------------------*/
delete network_client;
ClientTeardownMutex.unlock();
ClientTeardownCv.notify_all();
}
void ResourceManager::QueueClientTeardown()
{
ClientTeardownMutex.lock();
ClientTeardownPending = true;
ClientTeardownMutex.unlock();
ClientTeardownCv.notify_all();
}
void ResourceManager::ClientTeardownThreadFunction()
{
std::unique_lock<std::mutex> lock(ClientTeardownMutex);
while(ClientTeardownRunning)
{
if(!ClientTeardownPending)
{
ClientTeardownCv.wait(lock);
continue;
}
ClientTeardownPending = false;
/*-------------------------------------------------*\
| Take the clients queued for removal off the |
| pending list |
\*-------------------------------------------------*/
std::vector<NetworkClient*> remove_list;
remove_list.swap(clients_to_remove);
lock.unlock();
/*-------------------------------------------------*\
| Stop the clients being removed and take them out |
| of the clients list. StopClient joins the client |
| threads and moves the client's controllers to |
| orphaned_controllers; the delete below frees them |
\*-------------------------------------------------*/
for(std::size_t remove_idx = 0; remove_idx < remove_list.size(); remove_idx++)
{
remove_list[remove_idx]->StopClient();
remove_list[remove_idx]->ClearCallbacks();
/*---------------------------------------------*\
| If the removed client is the auto local |
| connection, leave client mode. Rescan and |
| GetDetection* use auto_connection_client, |
| which the delete below would leave pointing |
| at freed memory |
\*---------------------------------------------*/
if(remove_list[remove_idx] == auto_connection_client)
{
auto_connection_active = false;
auto_connection_client = NULL;
}
ClientListMutex.lock();
std::vector<NetworkClient*>::iterator client_it = std::find(clients.begin(), clients.end(), remove_list[remove_idx]);
if(client_it != clients.end())
{
clients.erase(client_it);
}
ClientListMutex.unlock();
}
/*-------------------------------------------------*\
| Collect the orphaned controllers from the clients |
| that remain. Ownership moves here, so a client |
| deleted later cannot free them a second time |
\*-------------------------------------------------*/
std::vector<RGBController*> orphaned;
ClientListMutex.lock();
for(std::size_t client_idx = 0; client_idx < clients.size(); client_idx++)
{
clients[client_idx]->TakeOrphanedControllers(orphaned);
}
ClientListMutex.unlock();
/*-------------------------------------------------*\
| Run the same sequence a rescan does while the |
| controllers are still allocated. Consumers |
| release them on DETECTION_STARTED, the GUI |
| deletes the device pages (whose destructors use |
| the controller) on UpdateDeviceList, and plugins |
| rebuild on DETECTION_COMPLETE. Free them last, |
| once nothing references them |
\*-------------------------------------------------*/
if(!remove_list.empty() || !orphaned.empty())
{
SignalResourceManagerUpdate(RESOURCEMANAGER_UPDATE_REASON_DETECTION_STARTED);
UpdateDeviceList();
SignalResourceManagerUpdate(RESOURCEMANAGER_UPDATE_REASON_DETECTION_COMPLETE);
/*---------------------------------------------*\
| Deleting a removed client frees the |
| controllers StopClient left on it |
\*---------------------------------------------*/
for(std::size_t remove_idx = 0; remove_idx < remove_list.size(); remove_idx++)
{
delete remove_list[remove_idx];
}
for(std::size_t orphaned_idx = 0; orphaned_idx < orphaned.size(); orphaned_idx++)
{
delete orphaned[orphaned_idx];
}
}
lock.lock();
}
UpdateDeviceList();
}
/*---------------------------------------------------------*\
@@ -893,8 +742,6 @@ void ResourceManager::UpdateDeviceList()
/*-----------------------------------------------------*\
| Insert client controllers into controller list |
\*-----------------------------------------------------*/
ClientListMutex.lock();
for(std::size_t client_idx = 0; client_idx < clients.size(); client_idx++)
{
std::vector<RGBController*> rgb_controllers_client = clients[client_idx]->GetRGBControllers();
@@ -905,8 +752,6 @@ void ResourceManager::UpdateDeviceList()
}
}
ClientListMutex.unlock();
/*-----------------------------------------------------*\
| Update server list |
\*-----------------------------------------------------*/
@@ -1110,9 +955,9 @@ void ResourceManager::Initialize(bool tryConnect, bool detectDevices, bool start
{
/*-----------------------------------------------------*\
| Cache the parameters |
| Don't try auto connect if starting server |
| TODO: Possibly cache them in the CLI file somewhere |
\*-----------------------------------------------------*/
tryAutoConnect = tryConnect && !startServer;
tryAutoConnect = tryConnect;
detection_enabled = detectDevices;
start_server = startServer;
apply_post_options = applyPostOptions;
@@ -1143,13 +988,6 @@ void ResourceManager::Initialize(bool tryConnect, bool detectDevices, bool start
tryAutoConnect = false;
}
/*-----------------------------------------------------*\
| Migrate legacy profiles after attempting local |
| connection so that migrated profiles get uploaded to |
| the server if connected |
\*-----------------------------------------------------*/
profile_manager->MigrateLegacyProfiles();
/*-----------------------------------------------------*\
| Initialize Saved Client Connections |
\*-----------------------------------------------------*/
-21
View File
@@ -13,9 +13,7 @@
#pragma once
#include <condition_variable>
#include <memory>
#include <mutex>
#include <vector>
#include <functional>
#include <thread>
@@ -104,7 +102,6 @@ public:
\*-----------------------------------------------------*/
void RegisterNetworkClient(NetworkClient* new_client);
void UnregisterNetworkClient(NetworkClient* network_client);
void QueueClientTeardown();
/*-----------------------------------------------------*\
| Local Client Accessors |
@@ -142,7 +139,6 @@ public:
private:
bool AttemptLocalConnection();
void ClientTeardownThreadFunction();
void SetupConfigurationDirectory();
/*-----------------------------------------------------*\
@@ -218,23 +214,6 @@ private:
| Network Clients |
\*-----------------------------------------------------*/
std::vector<NetworkClient*> clients;
std::mutex ClientListMutex;
/*-----------------------------------------------------*\
| Client teardown thread |
| |
| Frees controllers a lost connection left behind, |
| inside a DETECTION_STARTED / DETECTION_COMPLETE |
| sequence. Runs on its own thread: StopClient joins |
| the client's listener thread, and the sequence's |
| callbacks need the GUI thread free |
\*-----------------------------------------------------*/
std::thread* ClientTeardownThread;
std::mutex ClientTeardownMutex;
std::condition_variable ClientTeardownCv;
bool ClientTeardownPending;
bool ClientTeardownRunning;
std::vector<NetworkClient*> clients_to_remove;
/*-----------------------------------------------------*\
| Device List Mutex |
-1
View File
@@ -41,7 +41,6 @@
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define SD_RECEIVE SHUT_RD
#define SD_BOTH SHUT_RDWR
#endif
//Network Port Class
-2
View File
@@ -359,7 +359,6 @@
#define ASUS_ROG_STRIX_RTX_4090_O24G_GAMING_WHITE_2 0x88F1
#define ASUS_ROG_MATRIX_PLATINUM_RTX_4090_24G 0x8934
#define ASUS_TUF_RTX_5060_O8G_GAMING 0x8A1A
#define ASUS_TUF_RTX_5060TI_O16G_GAMING 0x8A08
#define ASUS_TUF_RTX_5070_O12G_GAMING 0x89F2
#define ASUS_TUF_RTX_5070TI_O16G_GAMING 0x89F4
#define ASUS_TUF_RTX_5070TI_O16G_GAMING_BTF_WHITE 0x8A37
@@ -440,7 +439,6 @@
#define COLORFUL_IGAME_RTX_3080TI_ADVANCED_OCV2 0x1401
#define COLORFUL_IGAME_RTX_3080_ULTRAW_OC_10G 0x1501
#define COLORFUL_IGAME_RTX_3080_ULTRAW_OC_10G_2 0x150A
#define COLORFUL_IGAME_RTX_4070_ULTRAW_OCV 0x1500
#define COLORFUL_IGAME_RTX_4070_VULCAN_OCV 0x1200
#define COLORFUL_IGAME_RTX_4070_SUPER_ULTRA_W_OC 0x1500
#define COLORFUL_IGAME_RTX_4070S_ULTRAW_OCV 0x1500
+27 -43
View File
@@ -1094,21 +1094,18 @@ void OpenRGBDevicePage::UpdateMode()
bool supports_dir_lr = 0;
bool supports_dir_ud = 0;
bool supports_dir_hv = 0;
bool supports_dir_diag = 0;
if(selected_zone_mode)
{
supports_dir_lr = ( device->GetZoneModeFlags(selected_zone, (unsigned int)selected_mode) & MODE_FLAG_HAS_DIRECTION_LR );
supports_dir_ud = ( device->GetZoneModeFlags(selected_zone, (unsigned int)selected_mode) & MODE_FLAG_HAS_DIRECTION_UD );
supports_dir_hv = ( device->GetZoneModeFlags(selected_zone, (unsigned int)selected_mode) & MODE_FLAG_HAS_DIRECTION_HV );
supports_dir_diag = ( device->GetZoneModeFlags(selected_zone, (unsigned int)selected_mode) & MODE_FLAG_HAS_DIRECTION_DIAG );
}
else
{
supports_dir_lr = ( device->GetModeFlags((unsigned int)selected_mode) & MODE_FLAG_HAS_DIRECTION_LR );
supports_dir_ud = ( device->GetModeFlags((unsigned int)selected_mode) & MODE_FLAG_HAS_DIRECTION_UD );
supports_dir_hv = ( device->GetModeFlags((unsigned int)selected_mode) & MODE_FLAG_HAS_DIRECTION_HV );
supports_dir_diag = ( device->GetModeFlags((unsigned int)selected_mode) & MODE_FLAG_HAS_DIRECTION_DIAG );
}
/*-----------------------------------------------------*\
@@ -1116,29 +1113,38 @@ void OpenRGBDevicePage::UpdateMode()
\*-----------------------------------------------------*/
if(ui->DirectionBox->isEnabled())
{
int offset = 0;
if(supports_dir_lr && current_dir_idx < offset + 2)
if(supports_dir_hv)
{
current_direction = current_dir_idx - offset;
if(supports_dir_lr && supports_dir_ud)
{
current_direction = current_dir_idx;
}
else if(supports_dir_lr || supports_dir_ud)
{
current_direction = current_dir_idx + 2;
}
else
{
current_direction = current_dir_idx + 4;
}
}
if(supports_dir_lr) offset += 2;
if(supports_dir_ud && current_dir_idx >= offset && current_dir_idx < offset + 2)
if(supports_dir_ud)
{
current_direction = MODE_DIRECTION_UP + (current_dir_idx - offset);
if(supports_dir_lr)
{
current_direction = current_dir_idx;
}
else
{
current_direction = current_dir_idx + 2;
}
}
if(supports_dir_ud) offset += 2;
if(supports_dir_hv && current_dir_idx >= offset && current_dir_idx < offset + 2)
if((supports_dir_lr)
&&(current_dir_idx < 2))
{
current_direction = MODE_DIRECTION_HORIZONTAL + (current_dir_idx - offset);
}
if(supports_dir_hv) offset += 2;
if(supports_dir_diag && current_dir_idx >= offset && current_dir_idx < offset + 4)
{
current_direction = MODE_DIRECTION_UP_LEFT + (current_dir_idx - offset);
current_direction = current_dir_idx;
}
if(selected_zone_mode)
@@ -1440,7 +1446,6 @@ void OpenRGBDevicePage::UpdateModeUi()
bool supports_dir_lr = flags & MODE_FLAG_HAS_DIRECTION_LR;
bool supports_dir_ud = flags & MODE_FLAG_HAS_DIRECTION_UD;
bool supports_dir_hv = flags & MODE_FLAG_HAS_DIRECTION_HV;
bool supports_dir_diag = flags & MODE_FLAG_HAS_DIRECTION_DIAG;
bool per_led = color_mode == MODE_COLORS_PER_LED;
bool mode_specific = color_mode == MODE_COLORS_MODE_SPECIFIC;
bool random = color_mode == MODE_COLORS_RANDOM;
@@ -1534,15 +1539,7 @@ void OpenRGBDevicePage::UpdateModeUi()
ui->DirectionBox->addItem(tr("Vertical"));
}
if(supports_dir_diag)
{
ui->DirectionBox->addItem(tr("Up-Left"));
ui->DirectionBox->addItem(tr("Up-Right"));
ui->DirectionBox->addItem(tr("Down-Left"));
ui->DirectionBox->addItem(tr("Down-Right"));
}
if(supports_dir_lr || supports_dir_ud || supports_dir_hv || supports_dir_diag)
if(supports_dir_lr || supports_dir_ud || supports_dir_hv)
{
if((supports_dir_lr)
&&((direction == MODE_DIRECTION_LEFT)
@@ -1583,19 +1580,6 @@ void OpenRGBDevicePage::UpdateModeUi()
}
}
if((supports_dir_diag)
&&((direction == MODE_DIRECTION_UP_LEFT)
||(direction == MODE_DIRECTION_UP_RIGHT)
||(direction == MODE_DIRECTION_DOWN_LEFT)
||(direction == MODE_DIRECTION_DOWN_RIGHT)))
{
int offset = 0;
if(supports_dir_lr) offset += 2;
if(supports_dir_ud) offset += 2;
if(supports_dir_hv) offset += 2;
ui->DirectionBox->setCurrentIndex(direction - MODE_DIRECTION_UP_LEFT + offset);
}
ui->DirectionBox->setEnabled(enable_controls);
}
else
@@ -2042,7 +2026,7 @@ void OpenRGBDevicePage::UpdateInterface(unsigned int update_reason)
case RGBCONTROLLER_UPDATE_REASON_HIDDEN:
case RGBCONTROLLER_UPDATE_REASON_UNHIDDEN:
case RGBCONTROLLER_UPDATE_REASON_CONFIGUREDEVICE:
emit RefreshList();
emit ShowHideList();
break;
case RGBCONTROLLER_UPDATE_REASON_UPDATELEDS:
+2
View File
@@ -129,5 +129,7 @@ private slots:
signals:
void RefreshList();
void ShowHideList();
void SetAllDevices(unsigned char red, unsigned char green, unsigned char blue);
};
+39 -42
View File
@@ -109,11 +109,12 @@ static void OpenRGBDialogResourceManagerCallback(void * this_ptr, unsigned int u
case RESOURCEMANAGER_UPDATE_REASON_DEVICE_LIST_UPDATED:
if(QThread::currentThread() != qApp->thread())
{
printf("invoking onDeviceListUpdated\n");
QMetaObject::invokeMethod(this_obj, "onDeviceListUpdated", Qt::BlockingQueuedConnection);
}
else
{
QMetaObject::invokeMethod(this_obj, "onDeviceListUpdated", Qt::DirectConnection);
this_obj->onDeviceListUpdated();
}
break;
}
@@ -968,11 +969,11 @@ void OpenRGBDialog::ClearDevicesList()
}
}
void OpenRGBDialog::ShowHideDevicesList()
void OpenRGBDialog::UpdateDevicesListShowHide()
{
/*-----------------------------------------------------*\
| Loop through all controller tabs and move any hidden |
| controller's page to the hidden pages vector |
| Loop through each controller in the tab list and hide |
| any controller marked as hidden. |
\*-----------------------------------------------------*/
for(int tab_idx = 0; tab_idx < ui->DevicesTabBar->count(); tab_idx++)
{
@@ -1007,40 +1008,40 @@ void OpenRGBDialog::ShowHideDevicesList()
}
/*-----------------------------------------------------*\
| Loop through all pages in the hidden pages vector and |
| move any no-longer-hidden controller's page back to |
| the tab bar |
| Loop through each controller in the hidden pages |
| vector and restore any controller marked as not |
| hidden. |
\*-----------------------------------------------------*/
for(std::size_t page_idx = 0; page_idx < hidden_pages.size(); page_idx++)
{
/*-------------------------------------------------*\
| Get a pointer to the page at this index in the |
| hidden pages vector |
\*-------------------------------------------------*/
OpenRGBDevicePage* page = hidden_pages[page_idx];
/*-----------------------------------------*\
| Get a pointer to the page at this index |
| in the hidden pages vector |
\*-----------------------------------------*/
OpenRGBDevicePage* page = (OpenRGBDevicePage*)hidden_pages[page_idx];
RGBController* controller = page->GetController();
/*-------------------------------------------------*\
| If the controller for this page is not hidden, |
| remove the page from the hidden pages vector and |
| add it to the tab bar |
\*-------------------------------------------------*/
if(!(page->GetController()->GetHidden()))
/*-----------------------------------------*\
| If the current tab matches the current |
| controller, check if it is hidden |
\*-----------------------------------------*/
if(!(controller->GetHidden()))
{
ui->DevicesTabBar->addTab(page, "");
ui->DevicesTabBar->addTab((QWidget*)page, "");
/*---------------------------------------------*\
| Create the tab label |
\*---------------------------------------------*/
TabLabel* NewTabLabel = new TabLabel(OpenRGBFont::GetIconIDFromDeviceType(page->GetController()->GetDeviceType()), (char *)page->GetController()->GetDisplayName().c_str(), (char *)context, false);
/*---------------------------------*\
| Create the tab label |
\*---------------------------------*/
TabLabel* NewTabLabel = new TabLabel(OpenRGBFont::GetIconIDFromDeviceType(controller->GetDeviceType()), (char *)controller->GetDisplayName().c_str(), (char *)context, false);
ui->DevicesTabBar->tabBar()->setTabButton(ui->DevicesTabBar->count() - 1, QTabBar::LeftSide, NewTabLabel);
hidden_pages.erase(hidden_pages.begin() + page_idx);
/*---------------------------------------------*\
| Decrement page index to account for removing |
| page |
\*---------------------------------------------*/
/*---------------------------------*\
| Decrement page index to account |
| for removing page |
\*---------------------------------*/
page_idx--;
}
}
@@ -1124,8 +1125,8 @@ void OpenRGBDialog::UpdateDevicesList()
\*-------------------------------------*/
else
{
ui->DevicesTabBar->tabBar()->moveTab(tab_idx, controller_tab_count);
controller_tab_count++;
ui->DevicesTabBar->tabBar()->moveTab(tab_idx, controller_idx);
}
break;
}
@@ -1226,6 +1227,11 @@ void OpenRGBDialog::UpdateDevicesList()
connect(NewPage,
SIGNAL(RefreshList()),
this,
SLOT(onDeviceListUpdated()));
connect(NewPage,
SIGNAL(ShowHideList()),
this,
SLOT(onDeviceListShowHide()));
if(controllers[controller_idx]->GetHidden())
@@ -1364,7 +1370,6 @@ void OpenRGBDialog::UpdateDevicesList()
}
bool found = true;
base_tab = controllers.size();
while(found)
{
@@ -1557,16 +1562,16 @@ void OpenRGBDialog::on_QuickWhite()
on_SetAllDevices(0xFF, 0xFF, 0xFF);
}
void OpenRGBDialog::onDeviceListShowHide()
{
ShowHideDevicesList();
}
void OpenRGBDialog::onDeviceListUpdated()
{
UpdateDevicesList();
}
void OpenRGBDialog::onDeviceListShowHide()
{
UpdateDevicesListShowHide();
}
void OpenRGBDialog::onDetectionProgressUpdated()
{
ui->DetectionProgressBar->setValue(ResourceManager::get()->GetDetectionPercent());
@@ -1604,14 +1609,6 @@ void OpenRGBDialog::onDetectionStarted()
void OpenRGBDialog::onDetectionEnded()
{
/*-----------------------------------------------------*\
| Always hide the detection view on |
| DETECTION_COMPLETE. The progress handler only hides |
| it at 100%, and a client teardown raises the |
| detection signals without setting the percent |
\*-----------------------------------------------------*/
SetDetectionViewState(false);
/*-----------------------------------------------------*\
| Detect unconfigured zones and prompt for resizing |
\*-----------------------------------------------------*/
+4 -2
View File
@@ -148,7 +148,7 @@ private:
void ClearDevicesList();
void UpdateDevicesList();
void ShowHideDevicesList();
void UpdateDevicesListShowHide();
void closeEvent(QCloseEvent *event) override;
bool SelectConfigProfile(const std::string name);
@@ -167,6 +167,9 @@ private:
void UpdateTabs();
bool isCompactTabMode();
public slots:
void onDeviceListUpdated();
private slots:
void on_Exit();
void on_LightsOff();
@@ -178,7 +181,6 @@ private slots:
void on_QuickMagenta();
void on_QuickWhite();
void onDeviceListShowHide();
void onDeviceListUpdated();
void onDetectionProgressUpdated();
void onDetectionStarted();
void onDetectionEnded();
-10
View File
@@ -8,15 +8,5 @@ Restart=always
RuntimeDirectory=openrgb
WorkingDirectory=/run/openrgb
ProtectSystem=strict
ProtectHome=true
PrivateDevices=false
ProtectKernelTunables=false
ProtectControlGroups=false
ReadWritePaths=/etc/openrgb
ReadWritePaths=/run/openrgb
[Install]
WantedBy=multi-user.target