Compare commits
41
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfaef77719 | ||
|
|
02d0e16a72 | ||
|
|
6ebf3573af | ||
|
|
5aef451e81 | ||
|
|
1880961afa | ||
|
|
fb5a723193 | ||
|
|
88002a4abf | ||
|
|
289dbc675a | ||
|
|
bcfaa7e8a7 | ||
|
|
bf62ead713 | ||
|
|
8a7cda7495 | ||
|
|
84cf974d70 | ||
|
|
afc54314b5 | ||
|
|
bd8db75106 | ||
|
|
9da692b12f | ||
|
|
4ee39b0dc8 | ||
|
|
286514b509 | ||
|
|
c7140baa33 | ||
|
|
2a9889b194 | ||
|
|
2afbd64c8e | ||
|
|
8be6aba43a | ||
|
|
565f1dad74 | ||
|
|
67a81f219d | ||
|
|
ccd103a29f | ||
|
|
c5d088b69d | ||
|
|
6362054368 | ||
|
|
2dab17f5dc | ||
|
|
c545305825 | ||
|
|
578c81784b | ||
|
|
1cd9b63c3e | ||
|
|
7b1817a0b8 | ||
|
|
fb0e4f3ad9 | ||
|
|
09a5b414b6 | ||
|
|
e5dc869c9b | ||
|
|
2687ff57aa | ||
|
|
52138b4cbd | ||
|
|
b3a2cccfed | ||
|
|
15a8abf2c9 | ||
|
|
d16b474aa8 | ||
|
|
760758230a | ||
|
|
358fc7b007 |
@@ -119,6 +119,21 @@ 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 |
|
||||
\*-----------------------------------------------------*/
|
||||
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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);
|
||||
};
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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);
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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();
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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;
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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);
|
||||
@@ -0,0 +1,80 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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;
|
||||
};
|
||||
@@ -0,0 +1,231 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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;
|
||||
};
|
||||
@@ -0,0 +1,152 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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,22 +1,25 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| ClevoKeyboardController.cpp |
|
||||
| |
|
||||
| Driver for Clevo laptop per-key RGB keyboard (ITE 8291) |
|
||||
| Protocol based on tuxedo-drivers ite_8291 module |
|
||||
| Driver for Clevo per-key RGB keyboard |
|
||||
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
|
||||
| |
|
||||
| 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)
|
||||
ClevoKeyboardController::ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info, clevo_keyboard_type kb_type)
|
||||
{
|
||||
dev = dev_handle;
|
||||
type = kb_type;
|
||||
location = info.path;
|
||||
version = info.release_number;
|
||||
}
|
||||
@@ -26,6 +29,31 @@ 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);
|
||||
@@ -51,147 +79,355 @@ std::string ClevoKeyboardController::GetFirmwareVersion()
|
||||
return(version_string);
|
||||
}
|
||||
|
||||
void ClevoKeyboardController::WriteControl(unsigned char* data)
|
||||
/*---------------------------------------------------------*\
|
||||
| ITE 8291: 8-byte feature report, no report ID |
|
||||
\*---------------------------------------------------------*/
|
||||
void ClevoKeyboardController::WriteControl8291(unsigned char* data)
|
||||
{
|
||||
hid_send_feature_report(dev, data, CLEVO_KEYBOARD_REPORT_SIZE);
|
||||
hid_send_feature_report(dev, data, CLEVO_8291_REPORT_SIZE);
|
||||
}
|
||||
|
||||
void ClevoKeyboardController::WriteRowData(unsigned char* data)
|
||||
{
|
||||
hid_write(dev, data, CLEVO_KEYBOARD_ROW_DATA_SIZE);
|
||||
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);
|
||||
}
|
||||
|
||||
void ClevoKeyboardController::TurnOff()
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Turn off: 08 01 00 00 00 00 00 00 |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE];
|
||||
if(type == CLEVO_KB_ITE829X)
|
||||
{
|
||||
SendCommand829x(CLEVO_829X_CMD_BRIGHTNESS);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
|
||||
buf[0] = 0x08;
|
||||
buf[1] = 0x01;
|
||||
|
||||
WriteControl(buf);
|
||||
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);
|
||||
}
|
||||
|
||||
void ClevoKeyboardController::SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char behaviour)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Set params: 08 [power] [mode] [speed] [brightness] 08 |
|
||||
| [behaviour] 00 |
|
||||
| power: 01=off, 02=on |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE];
|
||||
if(type == CLEVO_KB_ITE829X)
|
||||
{
|
||||
SendCommand829x(CLEVO_829X_CMD_BRIGHTNESS, brightness, speed);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
|
||||
buf[0] = 0x08;
|
||||
buf[1] = 0x02; // Power on
|
||||
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;
|
||||
buf[2] = mode;
|
||||
buf[3] = speed;
|
||||
buf[4] = brightness;
|
||||
buf[5] = 0x08;
|
||||
buf[5] = CLEVO_8291_MODE_FLAG;
|
||||
buf[6] = behaviour;
|
||||
|
||||
WriteControl(buf);
|
||||
WriteControl8291(buf);
|
||||
}
|
||||
|
||||
void ClevoKeyboardController::SetModeColor(unsigned char color_idx, unsigned char red, unsigned char green, unsigned char blue)
|
||||
void ClevoKeyboardController::SetModeColor(unsigned char color_idx, RGBColor color)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| 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];
|
||||
|
||||
memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
|
||||
buf[0] = 0x14;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = color_idx;
|
||||
buf[3] = red;
|
||||
buf[4] = green;
|
||||
buf[5] = blue;
|
||||
|
||||
WriteControl(buf);
|
||||
}
|
||||
|
||||
void ClevoKeyboardController::SendColors(unsigned char* color_data, unsigned char brightness)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| 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];
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Clamp brightness |
|
||||
\*---------------------------------------------------------*/
|
||||
if(brightness > CLEVO_KEYBOARD_BRIGHTNESS_MAX)
|
||||
if(type == CLEVO_KB_ITE829X)
|
||||
{
|
||||
brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char buf[CLEVO_8291_REPORT_SIZE];
|
||||
memset(buf, 0x00, CLEVO_8291_REPORT_SIZE);
|
||||
buf[0] = CLEVO_8291_CMD_COLOR;
|
||||
buf[2] = color_idx;
|
||||
buf[3] = RGBGetRValue(color);
|
||||
buf[4] = RGBGetGValue(color);
|
||||
buf[5] = RGBGetBValue(color);
|
||||
WriteControl8291(buf);
|
||||
}
|
||||
|
||||
void ClevoKeyboardController::SetBrightnessSpeed829x(unsigned char brightness, unsigned char speed)
|
||||
{
|
||||
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();
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Animation modes: send [CC, 00, mode_id] on mode change |
|
||||
\*---------------------------------------------------------*/
|
||||
struct anim_entry { unsigned char mode; unsigned char anim_id; };
|
||||
static const anim_entry anim_modes[] =
|
||||
{
|
||||
{ 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);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Set params for per-key mode |
|
||||
| Direction-based effects (Wave/Snake) |
|
||||
| Slot = base + direction_index |
|
||||
| Custom color: base = 0xA1, Preset: base = 0x71 |
|
||||
\*---------------------------------------------------------*/
|
||||
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;
|
||||
ctrl_buf[5] = 0x00;
|
||||
ctrl_buf[6] = 0x00;
|
||||
/*---------------------------------------------------------*\
|
||||
| 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 */
|
||||
};
|
||||
|
||||
WriteControl(ctrl_buf);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Send each row |
|
||||
| Breathing/Flashing: random or custom color |
|
||||
\*---------------------------------------------------------*/
|
||||
for(int row = 0; row < CLEVO_KEYBOARD_NUM_ROWS; row++)
|
||||
if(mode_value == CLEVO_829X_MODE_BREATH && use_custom_color)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Announce row data |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(ctrl_buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
|
||||
ctrl_buf[0] = 0x16;
|
||||
ctrl_buf[1] = 0x00;
|
||||
ctrl_buf[2] = row;
|
||||
SendColorSlot829x(0x0A, CLEVO_829X_COLOR_CUSTOM, colors[0]);
|
||||
}
|
||||
|
||||
WriteControl(ctrl_buf);
|
||||
if(mode_value == CLEVO_829X_MODE_BREATH && !use_custom_color)
|
||||
{
|
||||
SendCommand829x(0x0A);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Build row data buffer |
|
||||
| Format: [pad][pad][B0..B20][G0..G20][R0..R20] |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(row_buf, 0x00, CLEVO_KEYBOARD_ROW_DATA_SIZE);
|
||||
if(mode_value == CLEVO_829X_MODE_BLINK && use_custom_color)
|
||||
{
|
||||
SendColorSlot829x(0x0B, CLEVO_829X_COLOR_CUSTOM, colors[0]);
|
||||
}
|
||||
|
||||
for(int col = 0; col < CLEVO_KEYBOARD_NUM_COLS; col++)
|
||||
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++)
|
||||
{
|
||||
int led_idx = (row * CLEVO_KEYBOARD_NUM_COLS) + col;
|
||||
int color_offset = led_idx * 3;
|
||||
SendColorSlot829x(CLEVO_829X_CMD_SCAN_COLOR, CLEVO_829X_COLOR_SLOT_BASE + i, colors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char red = color_data[color_offset + 0];
|
||||
unsigned char green = color_data[color_offset + 1];
|
||||
unsigned char blue = color_data[color_offset + 2];
|
||||
/*---------------------------------------------------------*\
|
||||
| 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
|
||||
};
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| 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;
|
||||
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;
|
||||
ctrl_buf[4] = brightness;
|
||||
WriteControl8291(ctrl_buf);
|
||||
|
||||
for(int row = 0; row < num_rows; row++)
|
||||
{
|
||||
memset(ctrl_buf, 0x00, CLEVO_8291_REPORT_SIZE);
|
||||
ctrl_buf[0] = CLEVO_8291_CMD_ROW;
|
||||
ctrl_buf[2] = row;
|
||||
WriteControl8291(ctrl_buf);
|
||||
|
||||
memset(row_buf, 0x00, CLEVO_8291_ROW_DATA_SIZE);
|
||||
|
||||
for(int col = 0; col < num_cols; col++)
|
||||
{
|
||||
int idx = (row * num_cols + col) * 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];
|
||||
}
|
||||
|
||||
WriteRowData(row_buf);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| ClevoKeyboardController.h |
|
||||
| |
|
||||
| Driver for Clevo laptop per-key RGB keyboard (ITE 8291) |
|
||||
| Driver for Clevo per-key RGB keyboard |
|
||||
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
|
||||
| |
|
||||
| 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 |
|
||||
@@ -12,27 +14,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <hidapi.h>
|
||||
#include "RGBController.h"
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| ITE 8291 keyboard defines |
|
||||
\*-----------------------------------------------------*/
|
||||
#define CLEVO_KEYBOARD_REPORT_SIZE 8
|
||||
#define CLEVO_KEYBOARD_ROW_DATA_SIZE 65
|
||||
/*---------------------------------------------------------*\
|
||||
| Device type enumeration |
|
||||
\*---------------------------------------------------------*/
|
||||
enum clevo_keyboard_type
|
||||
{
|
||||
CLEVO_KB_ITE8291,
|
||||
CLEVO_KB_ITE829X,
|
||||
};
|
||||
|
||||
#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 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
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| 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_BRIGHTNESS_MAX 0x32
|
||||
|
||||
#define CLEVO_KEYBOARD_SPEED_MIN 0x01
|
||||
#define CLEVO_KEYBOARD_SPEED_MIN 0x00
|
||||
#define CLEVO_KEYBOARD_SPEED_MAX 0x0A
|
||||
#define CLEVO_KEYBOARD_SPEED_DEFAULT 0x00
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| ITE 8291 modes |
|
||||
\*-----------------------------------------------------*/
|
||||
/*---------------------------------------------------------*\
|
||||
| ITE 8291 modes |
|
||||
\*---------------------------------------------------------*/
|
||||
enum
|
||||
{
|
||||
CLEVO_KEYBOARD_MODE_DIRECT = 0x33,
|
||||
@@ -47,9 +97,36 @@ enum
|
||||
CLEVO_KEYBOARD_MODE_SPARK = 0x11,
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Wave/reactive behaviour |
|
||||
\*-----------------------------------------------------*/
|
||||
/*---------------------------------------------------------*\
|
||||
| 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 |
|
||||
\*---------------------------------------------------------*/
|
||||
enum
|
||||
{
|
||||
CLEVO_KEYBOARD_DIRECTION_LEFT = 0x01,
|
||||
@@ -67,23 +144,40 @@ enum
|
||||
class ClevoKeyboardController
|
||||
{
|
||||
public:
|
||||
ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info);
|
||||
ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info, clevo_keyboard_type kb_type);
|
||||
~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, unsigned char red, unsigned char green, unsigned char blue);
|
||||
void SendColors(unsigned char* color_data, unsigned char brightness);
|
||||
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();
|
||||
|
||||
private:
|
||||
hid_device* dev;
|
||||
std::string location;
|
||||
unsigned short version;
|
||||
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;
|
||||
|
||||
void WriteControl(unsigned char* data);
|
||||
void WriteControl8291(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,9 +1,11 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| ClevoKeyboardControllerDetect.cpp |
|
||||
| |
|
||||
| Detector for Clevo per-key RGB keyboard (ITE 8291) |
|
||||
| Detector for Clevo per-key RGB keyboard |
|
||||
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
|
||||
| |
|
||||
| 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 |
|
||||
@@ -22,11 +24,12 @@
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Clevo Keyboard product IDs |
|
||||
| These are ITE 8291 per-key RGB keyboard controllers |
|
||||
| ITE 8291 and ITE 829x per-key RGB keyboard controllers |
|
||||
\*---------------------------------------------------------*/
|
||||
#define CLEVO_KEYBOARD_PID_600B 0x600B
|
||||
#define CLEVO_KEYBOARD_PID_8291 0x600B
|
||||
#define CLEVO_KEYBOARD_PID_829X 0x8910
|
||||
|
||||
DetectedControllers DetectClevoKeyboardControllers(hid_device_info* info, const std::string& /*name*/)
|
||||
static DetectedControllers DetectClevoKeyboard(hid_device_info* info, clevo_keyboard_type kb_type)
|
||||
{
|
||||
DetectedControllers detected_controllers;
|
||||
hid_device* dev;
|
||||
@@ -35,7 +38,7 @@ DetectedControllers DetectClevoKeyboardControllers(hid_device_info* info, const
|
||||
|
||||
if(dev)
|
||||
{
|
||||
ClevoKeyboardController* controller = new ClevoKeyboardController(dev, *info);
|
||||
ClevoKeyboardController* controller = new ClevoKeyboardController(dev, *info, kb_type);
|
||||
RGBController_ClevoKeyboard* rgb_controller = new RGBController_ClevoKeyboard(controller);
|
||||
|
||||
detected_controllers.push_back(rgb_controller);
|
||||
@@ -44,4 +47,15 @@ DetectedControllers DetectClevoKeyboardControllers(hid_device_info* info, const
|
||||
return(detected_controllers);
|
||||
}
|
||||
|
||||
REGISTER_HID_DETECTOR_PU("CLEVO Keyboard", DetectClevoKeyboardControllers, 0x048D, 0x600B, 0xFF03, 0x01);
|
||||
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);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| ClevoKeyboardDevices.cpp |
|
||||
| |
|
||||
| Device list for Clevo per-key RGB keyboards (ITE 8291) |
|
||||
| Device list for Clevo per-key RGB keyboards |
|
||||
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
|
||||
| |
|
||||
| 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 |
|
||||
@@ -112,6 +114,48 @@ 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,
|
||||
@@ -122,10 +166,9 @@ keyboard_keymap_overlay_values clevo_keyboard_layout
|
||||
}
|
||||
},
|
||||
{
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
| Edit Keys |
|
||||
| Zone, Row, Column, Value, Key, Alternate Name, OpCode, |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
/*---------------------------------------------------------*\
|
||||
| Edit Keys |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Remove keys not present on Clevo keyboard |
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| ClevoKeyboardDevices.h |
|
||||
| |
|
||||
| Device list for Clevo per-key RGB keyboards (ITE 8291) |
|
||||
| Device list for Clevo per-key RGB keyboards |
|
||||
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
|
||||
| |
|
||||
| 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 |
|
||||
@@ -14,7 +16,8 @@
|
||||
#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,14 +1,17 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_ClevoKeyboard.cpp |
|
||||
| |
|
||||
| RGBController for Clevo per-key RGB keyboard (ITE 8291) |
|
||||
| RGBController for Clevo per-key RGB keyboard |
|
||||
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
|
||||
| |
|
||||
| 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"
|
||||
|
||||
@@ -40,152 +43,306 @@ 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 = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
|
||||
Direct.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
|
||||
Direct.brightness_max = controller->GetBrightnessMax();
|
||||
Direct.brightness = controller->GetBrightnessMax();
|
||||
Direct.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Direct);
|
||||
|
||||
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);
|
||||
/*---------------------------------------------------------*\
|
||||
| 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Off;
|
||||
Off.name = "Off";
|
||||
@@ -207,16 +364,22 @@ RGBController_ClevoKeyboard::~RGBController_ClevoKeyboard()
|
||||
void RGBController_ClevoKeyboard::SetupZones()
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Create keyboard layout using KeyboardLayoutManager |
|
||||
| Select layout based on device type |
|
||||
\*---------------------------------------------------------*/
|
||||
KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ISO_QWERTY,
|
||||
clevo_keyboard_layout.base_size,
|
||||
clevo_keyboard_layout.key_values);
|
||||
bool is_829x = (controller->GetType() == CLEVO_KB_ITE829X);
|
||||
|
||||
new_kb.ChangeKeys(clevo_keyboard_layout);
|
||||
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);
|
||||
|
||||
new_kb.ChangeKeys(layout);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Create a matrix zone for the keyboard |
|
||||
| Create keyboard zone from KLM |
|
||||
\*---------------------------------------------------------*/
|
||||
zone keyboard_zone;
|
||||
|
||||
@@ -233,9 +396,6 @@ 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;
|
||||
@@ -249,36 +409,48 @@ void RGBController_ClevoKeyboard::SetupZones()
|
||||
SetupColors();
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Create buffer map to translate OpenRGB LED order to |
|
||||
| hardware LED order. The hardware expects 126 color values |
|
||||
| indexed by LED position (0-125). |
|
||||
| Build buffer map from LED values to color pointers |
|
||||
| 829x LED IDs have gaps (stride 32), need larger map |
|
||||
\*---------------------------------------------------------*/
|
||||
null_color = 0x00000000;
|
||||
buffer_map.resize(CLEVO_KEYBOARD_NUM_LEDS, &null_color);
|
||||
|
||||
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);
|
||||
|
||||
for(size_t led_idx = 0; led_idx < leds.size(); led_idx++)
|
||||
{
|
||||
buffer_map[leds[led_idx].value] = &colors[led_idx];
|
||||
if(leds[led_idx].value < buffer_map.size())
|
||||
{
|
||||
buffer_map[leds[led_idx].value] = &colors[led_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RGBController_ClevoKeyboard::DeviceUpdateLEDs()
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| 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];
|
||||
int num_rows = controller->GetNumRows();
|
||||
int num_cols = controller->GetNumCols();
|
||||
bool is_829x = (controller->GetType() == CLEVO_KB_ITE829X);
|
||||
|
||||
for(int i = 0; i < CLEVO_KEYBOARD_NUM_LEDS; i++)
|
||||
std::vector<unsigned char> color_data(num_rows * num_cols * 3);
|
||||
|
||||
for(int row = 0; row < num_rows; row++)
|
||||
{
|
||||
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]);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
controller->SendColors(color_data, modes[active_mode].brightness);
|
||||
controller->SendColors(color_data.data(), modes[active_mode].brightness, modes[active_mode].speed);
|
||||
}
|
||||
|
||||
void RGBController_ClevoKeyboard::DeviceUpdateZoneLEDs(int /*zone*/)
|
||||
@@ -304,6 +476,16 @@ 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 |
|
||||
\*---------------------------------------------------------*/
|
||||
@@ -318,41 +500,26 @@ void RGBController_ClevoKeyboard::DeviceUpdateMode()
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned char brightness = modes[active_mode].brightness;
|
||||
unsigned char speed = modes[active_mode].speed;
|
||||
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;
|
||||
}
|
||||
}
|
||||
unsigned char behaviour = (mode_value == CLEVO_KEYBOARD_MODE_WAVE)
|
||||
? modes[active_mode].direction + 1
|
||||
: 0x00;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Set mode color if applicable |
|
||||
\*---------------------------------------------------------*/
|
||||
if(modes[active_mode].colors.size() > 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->SetModeColor(1, modes[active_mode].colors[0]);
|
||||
}
|
||||
|
||||
controller->SetMode(mode_value, brightness, speed, behaviour);
|
||||
}
|
||||
|
||||
void RGBController_ClevoKeyboard::DeviceSaveMode()
|
||||
{
|
||||
if(controller->GetType() == CLEVO_KB_ITE829X)
|
||||
{
|
||||
DeviceUpdateMode();
|
||||
controller->SaveToBios829x();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_ClevoKeyboard.h |
|
||||
| |
|
||||
| RGBController for Clevo per-key RGB keyboard (ITE 8291) |
|
||||
| RGBController for Clevo per-key RGB keyboard |
|
||||
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
|
||||
| |
|
||||
| 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 |
|
||||
@@ -18,7 +20,7 @@
|
||||
class RGBController_ClevoKeyboard : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_ClevoKeyboard(ClevoKeyboardController* controller_ptr);
|
||||
explicit RGBController_ClevoKeyboard(ClevoKeyboardController* controller_ptr);
|
||||
~RGBController_ClevoKeyboard();
|
||||
|
||||
void SetupZones();
|
||||
@@ -28,6 +30,7 @@ public:
|
||||
void DeviceUpdateSingleLED(int led);
|
||||
|
||||
void DeviceUpdateMode();
|
||||
void DeviceSaveMode();
|
||||
|
||||
private:
|
||||
ClevoKeyboardController* controller;
|
||||
|
||||
@@ -71,6 +71,7 @@ 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,6 +411,7 @@ 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);
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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);
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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);
|
||||
@@ -0,0 +1,182 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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;
|
||||
};
|
||||
@@ -0,0 +1,173 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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;
|
||||
};
|
||||
@@ -1,133 +0,0 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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);
|
||||
+3
-2
@@ -44,8 +44,9 @@ 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[2] != 0x01)
|
||||
if(res < 0 || data_readpkt[0] != 0x01 || (data_readpkt[1] != 0x01 && data_readpkt[1] != 0x02) || data_readpkt[2] != 0x01)
|
||||
{
|
||||
// Assemble C-string with respons for debugging
|
||||
std::string text = "";
|
||||
@@ -57,7 +58,7 @@ bool TestForGigabyteRGBFusion2BlackwellGPUController(i2c_smbus_interface* bus, u
|
||||
text.append(str);
|
||||
}
|
||||
|
||||
LOG_DEBUG("[%s] at address 0x%02X invalid. Expected 0x01 0x01 0x01 [0x*] but received:%s", GIGABYTEGPU_CONTROLLER_NAME3, address, text.c_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());
|
||||
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,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
"WIFI Antenna"
|
||||
};
|
||||
|
||||
@@ -3930,6 +3930,23 @@ 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,
|
||||
@@ -4542,6 +4559,14 @@ 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,
|
||||
@@ -4894,48 +4919,13 @@ 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_37_device =
|
||||
static gb_fusion2_layout it5702_36_device =
|
||||
{
|
||||
&common_d_led1_zone,
|
||||
&common_d_led2_zone,
|
||||
@@ -4953,7 +4943,7 @@ static gb_fusion2_layout it5702_37_device =
|
||||
|
||||
static const gb_fusion2_device b650_aor_pro_ax_device =
|
||||
{
|
||||
&it5702_37_device,
|
||||
&it5702_36_device,
|
||||
0x09A001DF,
|
||||
0,
|
||||
"B650 AORUS PRO AX",
|
||||
@@ -4961,7 +4951,7 @@ static const gb_fusion2_device b650_aor_pro_ax_device =
|
||||
|
||||
static const gb_fusion2_device b650e_aor_mstr_device =
|
||||
{
|
||||
&it5702_37_device,
|
||||
&it5702_36_device,
|
||||
0x09A001DF,
|
||||
0,
|
||||
"B650E AORUS MASTER",
|
||||
@@ -4969,14 +4959,14 @@ static const gb_fusion2_device b650e_aor_mstr_device =
|
||||
|
||||
static const gb_fusion2_device x670e_aor_mstr_device =
|
||||
{
|
||||
&it5702_37_device,
|
||||
&it5702_36_device,
|
||||
0x024001DF,
|
||||
0,
|
||||
"X670E AORUS MASTER",
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------*\
|
||||
| Layout 38 048D:5702 |
|
||||
| Layout 37 048D:5702 |
|
||||
| |
|
||||
| Zone "D_LED1" : Linear |
|
||||
| Zone "D_LED2" : Linear |
|
||||
@@ -4986,7 +4976,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_38_device =
|
||||
static gb_fusion2_layout it5702_37_device =
|
||||
{
|
||||
&common_d_led1_zone,
|
||||
&common_d_led2_zone,
|
||||
@@ -5004,7 +4994,7 @@ static gb_fusion2_layout it5702_38_device =
|
||||
|
||||
static const gb_fusion2_device b660_aor_mstr_device =
|
||||
{
|
||||
&it5702_38_device,
|
||||
&it5702_37_device,
|
||||
0x094001DF,
|
||||
0,
|
||||
"B660 AORUS MASTER",
|
||||
@@ -5012,7 +5002,7 @@ static const gb_fusion2_device b660_aor_mstr_device =
|
||||
|
||||
static const gb_fusion2_device b660_aor_mstr_ddr4_device =
|
||||
{
|
||||
&it5702_38_device,
|
||||
&it5702_37_device,
|
||||
0x094001DF,
|
||||
0,
|
||||
"B660 AORUS MASTER DDR4",
|
||||
@@ -5020,7 +5010,7 @@ static const gb_fusion2_device b660_aor_mstr_ddr4_device =
|
||||
|
||||
static const gb_fusion2_device b760_aor_mstr_ddr4_device =
|
||||
{
|
||||
&it5702_38_device,
|
||||
&it5702_37_device,
|
||||
0x094001DF,
|
||||
0,
|
||||
"B760 AORUS MASTER DDR4",
|
||||
@@ -5028,7 +5018,7 @@ static const gb_fusion2_device b760_aor_mstr_ddr4_device =
|
||||
|
||||
static const gb_fusion2_device z690_aor_elite_device =
|
||||
{
|
||||
&it5702_38_device,
|
||||
&it5702_37_device,
|
||||
0x016001DF,
|
||||
0,
|
||||
"Z690 AORUS ELITE",
|
||||
@@ -5036,7 +5026,7 @@ static const gb_fusion2_device z690_aor_elite_device =
|
||||
|
||||
static const gb_fusion2_device z690_aor_elite_ax_2_device =
|
||||
{
|
||||
&it5702_38_device,
|
||||
&it5702_37_device,
|
||||
0x016001DF,
|
||||
0,
|
||||
"Z690 AORUS ELITE AX",
|
||||
@@ -5044,7 +5034,7 @@ static const gb_fusion2_device z690_aor_elite_ax_2_device =
|
||||
|
||||
static const gb_fusion2_device z690_aor_elite_ax_ddr4_device =
|
||||
{
|
||||
&it5702_38_device,
|
||||
&it5702_37_device,
|
||||
0x016001DF,
|
||||
0,
|
||||
"Z690 AORUS ELITE AX DDR4",
|
||||
@@ -5052,7 +5042,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_38_device,
|
||||
&it5702_37_device,
|
||||
0x016001DF,
|
||||
0,
|
||||
"Z690 AORUS ELITE AX DDR4 V2",
|
||||
@@ -5060,7 +5050,7 @@ static const gb_fusion2_device z690_aor_elite_ax_ddr4_v2_device =
|
||||
|
||||
static const gb_fusion2_device z690_aor_elite_ddr4_device =
|
||||
{
|
||||
&it5702_38_device,
|
||||
&it5702_37_device,
|
||||
0x016001DF,
|
||||
0,
|
||||
"Z690 AORUS ELITE DDR4",
|
||||
@@ -5077,7 +5067,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_39_device =
|
||||
static gb_fusion2_layout it5702_38_device =
|
||||
{
|
||||
&common_d_led1_zone,
|
||||
&common_d_led2_zone,
|
||||
@@ -5095,7 +5085,7 @@ static gb_fusion2_layout it5702_39_device =
|
||||
|
||||
static const gb_fusion2_device b550_aor_elite_device =
|
||||
{
|
||||
&it5702_39_device,
|
||||
&it5702_38_device,
|
||||
0x0170005F,
|
||||
0,
|
||||
"B550 AORUS ELITE",
|
||||
@@ -5103,7 +5093,7 @@ static const gb_fusion2_device b550_aor_elite_device =
|
||||
|
||||
static const gb_fusion2_device b550_aor_elite_ax_device =
|
||||
{
|
||||
&it5702_39_device,
|
||||
&it5702_38_device,
|
||||
0x0170005F,
|
||||
0,
|
||||
"B550 AORUS ELITE AX",
|
||||
@@ -5111,7 +5101,7 @@ static const gb_fusion2_device b550_aor_elite_ax_device =
|
||||
|
||||
static const gb_fusion2_device b550_aor_elite_ax_v2_device =
|
||||
{
|
||||
&it5702_39_device,
|
||||
&it5702_38_device,
|
||||
0x0170005F,
|
||||
0,
|
||||
"B550 AORUS ELITE AX V2",
|
||||
@@ -5119,7 +5109,7 @@ static const gb_fusion2_device b550_aor_elite_ax_v2_device =
|
||||
|
||||
static const gb_fusion2_device b550_aor_elite_ax_v3_device =
|
||||
{
|
||||
&it5702_39_device,
|
||||
&it5702_38_device,
|
||||
0x0170005F,
|
||||
0,
|
||||
"B550 AORUS ELITE AX V3",
|
||||
@@ -5127,7 +5117,7 @@ static const gb_fusion2_device b550_aor_elite_ax_v3_device =
|
||||
|
||||
static const gb_fusion2_device b550_aor_elite_v2_device =
|
||||
{
|
||||
&it5702_39_device,
|
||||
&it5702_38_device,
|
||||
0x0170005F,
|
||||
0,
|
||||
"B550 AORUS ELITE V2",
|
||||
@@ -5135,7 +5125,7 @@ static const gb_fusion2_device b550_aor_elite_v2_device =
|
||||
|
||||
static const gb_fusion2_device b550_aor_pro_device =
|
||||
{
|
||||
&it5702_39_device,
|
||||
&it5702_38_device,
|
||||
0x0170005F,
|
||||
0,
|
||||
"B550 AORUS PRO",
|
||||
@@ -5143,7 +5133,7 @@ static const gb_fusion2_device b550_aor_pro_device =
|
||||
|
||||
static const gb_fusion2_device b550_aor_pro_ac_device =
|
||||
{
|
||||
&it5702_39_device,
|
||||
&it5702_38_device,
|
||||
0x0170005F,
|
||||
0,
|
||||
"B550 AORUS PRO AC",
|
||||
@@ -5151,7 +5141,7 @@ static const gb_fusion2_device b550_aor_pro_ac_device =
|
||||
|
||||
static const gb_fusion2_device b550_aor_pro_ax_device =
|
||||
{
|
||||
&it5702_39_device,
|
||||
&it5702_38_device,
|
||||
0x0170005F,
|
||||
0,
|
||||
"B550 AORUS PRO AX",
|
||||
@@ -5159,7 +5149,7 @@ static const gb_fusion2_device b550_aor_pro_ax_device =
|
||||
|
||||
static const gb_fusion2_device b550_aor_pro_v2_device =
|
||||
{
|
||||
&it5702_39_device,
|
||||
&it5702_38_device,
|
||||
0x0170005F,
|
||||
0,
|
||||
"B550 AORUS PRO V2",
|
||||
@@ -5167,7 +5157,7 @@ static const gb_fusion2_device b550_aor_pro_v2_device =
|
||||
|
||||
static const gb_fusion2_device x570s_aor_pro_ax_device =
|
||||
{
|
||||
&it5702_39_device,
|
||||
&it5702_38_device,
|
||||
0x0150015F,
|
||||
0,
|
||||
"X570S AORUS PRO AX",
|
||||
@@ -5178,7 +5168,7 @@ static const gb_fusion2_device x570s_aor_pro_ax_device =
|
||||
| |
|
||||
| Zone "IO Cover" : Single |
|
||||
\*-------------------------------------------------------------*/
|
||||
static gb_fusion2_layout it5702_40_device =
|
||||
static gb_fusion2_layout it5702_39_device =
|
||||
{
|
||||
&common_io_cov_1_zone,
|
||||
nullptr,
|
||||
@@ -5196,7 +5186,7 @@ static gb_fusion2_layout it5702_40_device =
|
||||
|
||||
static const gb_fusion2_device z790_aor_mstr_x_2_device =
|
||||
{
|
||||
&it5702_40_device,
|
||||
&it5702_39_device,
|
||||
0x123001DF,
|
||||
1,
|
||||
"Z790 AORUS MASTER X",
|
||||
@@ -5204,7 +5194,7 @@ static const gb_fusion2_device z790_aor_mstr_x_2_device =
|
||||
|
||||
static const gb_fusion2_device z790_aor_mstr_2_device =
|
||||
{
|
||||
&it5702_40_device,
|
||||
&it5702_39_device,
|
||||
0x015001DF,
|
||||
1,
|
||||
"Z790 AORUS MASTER",
|
||||
@@ -5212,7 +5202,7 @@ static const gb_fusion2_device z790_aor_mstr_2_device =
|
||||
|
||||
static const gb_fusion2_device x670_aor_xtrm_2_device =
|
||||
{
|
||||
&it5702_40_device,
|
||||
&it5702_39_device,
|
||||
0x023001DF,
|
||||
1,
|
||||
"X670E AORUS XTREME",
|
||||
@@ -5223,7 +5213,7 @@ static const gb_fusion2_device x670_aor_xtrm_2_device =
|
||||
| |
|
||||
| Zone "Chipset Accent" : Single |
|
||||
\*-------------------------------------------------------------*/
|
||||
static gb_fusion2_layout it5702_41_device =
|
||||
static gb_fusion2_layout it5702_40_device =
|
||||
{
|
||||
&common_chip_acc_3_zone,
|
||||
nullptr,
|
||||
@@ -5241,7 +5231,7 @@ static gb_fusion2_layout it5702_41_device =
|
||||
|
||||
static const gb_fusion2_device z790_aor_xtrm_2_device =
|
||||
{
|
||||
&it5702_41_device,
|
||||
&it5702_40_device,
|
||||
0x026001DF,
|
||||
1,
|
||||
"Z790 AORUS XTREME",
|
||||
@@ -5249,7 +5239,7 @@ static const gb_fusion2_device z790_aor_xtrm_2_device =
|
||||
|
||||
static const gb_fusion2_device z790_aor_xtrm_x_2_device =
|
||||
{
|
||||
&it5702_41_device,
|
||||
&it5702_40_device,
|
||||
0x124001DF,
|
||||
1,
|
||||
"Z790 AORUS XTREME X",
|
||||
@@ -5261,7 +5251,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_42_device =
|
||||
static gb_fusion2_layout it5702_41_device =
|
||||
{
|
||||
&common_chip_acc_3_zone,
|
||||
&common_led_cpu_8_zone,
|
||||
@@ -5279,7 +5269,7 @@ static gb_fusion2_layout it5702_42_device =
|
||||
|
||||
static const gb_fusion2_device z690_aor_xtrm_waterforce_2_device =
|
||||
{
|
||||
&it5702_42_device,
|
||||
&it5702_41_device,
|
||||
0x029001DF,
|
||||
1,
|
||||
"Z690 AORUS XTREME WATERFORCE",
|
||||
@@ -5292,7 +5282,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_43_device =
|
||||
static gb_fusion2_layout it5702_42_device =
|
||||
{
|
||||
&common_chip_acc_3_zone,
|
||||
&common_chip_acc_3_zone,
|
||||
@@ -5310,7 +5300,7 @@ static gb_fusion2_layout it5702_43_device =
|
||||
|
||||
static const gb_fusion2_device z490_aor_xtrm_2_device =
|
||||
{
|
||||
&it5702_43_device,
|
||||
&it5702_42_device,
|
||||
0x029001DF,
|
||||
1,
|
||||
"Z490 AORUS XTREME",
|
||||
@@ -5318,7 +5308,7 @@ static const gb_fusion2_device z490_aor_xtrm_2_device =
|
||||
|
||||
static const gb_fusion2_device z490_aor_xtrm_waterforce_2_device =
|
||||
{
|
||||
&it5702_43_device,
|
||||
&it5702_42_device,
|
||||
0x029001DF,
|
||||
1,
|
||||
"Z490 AORUS XTREME WATERFORCE",
|
||||
@@ -5326,7 +5316,7 @@ static const gb_fusion2_device z490_aor_xtrm_waterforce_2_device =
|
||||
|
||||
static const gb_fusion2_device z590_aor_xtrm_waterforce_2_device =
|
||||
{
|
||||
&it5702_43_device,
|
||||
&it5702_42_device,
|
||||
0x029001DF,
|
||||
1,
|
||||
"Z590 AORUS XTREME WATERFORCE",
|
||||
@@ -5340,7 +5330,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_44_device =
|
||||
static gb_fusion2_layout it5702_43_device =
|
||||
{
|
||||
&common_chip_acc_3_zone,
|
||||
&common_chip_acc_3_zone,
|
||||
@@ -5358,7 +5348,7 @@ static gb_fusion2_layout it5702_44_device =
|
||||
|
||||
static const gb_fusion2_device z690_aor_xtrm_2_device =
|
||||
{
|
||||
&it5702_44_device,
|
||||
&it5702_43_device,
|
||||
0x028001DF,
|
||||
1,
|
||||
"Z690 AORUS XTREME",
|
||||
@@ -5375,7 +5365,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_45_device =
|
||||
static gb_fusion2_layout it5702_44_device =
|
||||
{
|
||||
&common_d_led1_zone,
|
||||
&common_d_led2_zone,
|
||||
@@ -5393,7 +5383,7 @@ static gb_fusion2_layout it5702_45_device =
|
||||
|
||||
static const gb_fusion2_device z690_aor_mstr_device =
|
||||
{
|
||||
&it5702_45_device,
|
||||
&it5702_44_device,
|
||||
0x017001DF,
|
||||
0,
|
||||
"Z690 AORUS MASTER",
|
||||
@@ -5409,7 +5399,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_46_device =
|
||||
static gb_fusion2_layout it5702_45_device =
|
||||
{
|
||||
&common_d_led1_zone,
|
||||
&common_d_led2_zone,
|
||||
@@ -5427,7 +5417,7 @@ static gb_fusion2_layout it5702_46_device =
|
||||
|
||||
static const gb_fusion2_device x570s_aor_elite_device =
|
||||
{
|
||||
&it5702_46_device,
|
||||
&it5702_45_device,
|
||||
0x0120015F,
|
||||
0,
|
||||
"X570S AORUS ELITE",
|
||||
@@ -5435,7 +5425,7 @@ static const gb_fusion2_device x570s_aor_elite_device =
|
||||
|
||||
static const gb_fusion2_device x570s_aor_elite_ax_device =
|
||||
{
|
||||
&it5702_46_device,
|
||||
&it5702_45_device,
|
||||
0x0120015F,
|
||||
0,
|
||||
"X570S AORUS ELITE AX",
|
||||
@@ -5443,7 +5433,7 @@ static const gb_fusion2_device x570s_aor_elite_ax_device =
|
||||
|
||||
static const gb_fusion2_device x570s_gmg_x_device =
|
||||
{
|
||||
&it5702_46_device,
|
||||
&it5702_45_device,
|
||||
0x0120015F,
|
||||
0,
|
||||
"X570S GAMING X",
|
||||
@@ -5458,7 +5448,7 @@ static const gb_fusion2_device x570s_gmg_x_device =
|
||||
| Zone "Aorus Logo" : Single |
|
||||
| Zone "LED_C2" : Single |
|
||||
\*-------------------------------------------------------------*/
|
||||
static gb_fusion2_layout it5702_47_device =
|
||||
static gb_fusion2_layout it5702_46_device =
|
||||
{
|
||||
&common_d_led1_zone,
|
||||
&common_d_led2_zone,
|
||||
@@ -5475,7 +5465,7 @@ static gb_fusion2_layout it5702_47_device =
|
||||
|
||||
static const gb_fusion2_device a520m_aor_elite_device =
|
||||
{
|
||||
&it5702_47_device,
|
||||
&it5702_46_device,
|
||||
0x0050005F,
|
||||
0,
|
||||
"A520M AORUS ELITE",
|
||||
@@ -6987,7 +6977,6 @@ 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 |
|
||||
@@ -6998,7 +6987,6 @@ 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,
|
||||
@@ -7007,6 +6995,7 @@ static gb_fusion2_layout it5711_14_device =
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
};
|
||||
|
||||
static const gb_fusion2_device x870e_aor_xtrm_ai_top_5711_device =
|
||||
@@ -7393,6 +7382,8 @@ 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 == zone_at_idx->leds_max);
|
||||
bool fixed_zone = (zone_at_idx->leds_min == 1 && zone_at_idx->leds_max == 1);
|
||||
|
||||
const Gen2StripInfo* gen2_info = nullptr;
|
||||
|
||||
|
||||
@@ -208,7 +208,8 @@ void NanoleafController::UpdateLEDs(std::vector<RGBColor>& colors)
|
||||
free(message);
|
||||
}
|
||||
else if((model == NANOLEAF_CANVAS_MODEL)
|
||||
|| (model == NANOLEAF_SHAPES_MODEL))
|
||||
|| (model == NANOLEAF_SHAPES_MODEL)
|
||||
|| (model == NANOLEAF_LINES_MODEL))
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Protocol V2 - https://forum.nanoleaf.me/docs |
|
||||
@@ -275,7 +276,8 @@ void NanoleafController::StartExternalControl()
|
||||
}
|
||||
}
|
||||
else if((model == NANOLEAF_CANVAS_MODEL)
|
||||
|| (model == NANOLEAF_SHAPES_MODEL))
|
||||
|| (model == NANOLEAF_SHAPES_MODEL)
|
||||
|| (model == NANOLEAF_LINES_MODEL))
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Protocol v2 does not return anything, use device IP and |
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define NANOLEAF_LIGHT_PANELS_MODEL "NL22"
|
||||
#define NANOLEAF_CANVAS_MODEL "NL29"
|
||||
#define NANOLEAF_SHAPES_MODEL "NL42"
|
||||
#define NANOLEAF_LINES_MODEL "NL59"
|
||||
|
||||
class NanoleafController
|
||||
{
|
||||
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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();
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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;
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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()
|
||||
{
|
||||
}
|
||||
+3
-29
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <hidapi.h>
|
||||
#include "../RoyuanKeyboardController/RoyuanKeyboardController.h"
|
||||
|
||||
#define EPOMAKER_PACKET_LENGTH 0x40
|
||||
#define EPOMAKER_COMMAND_RGB 0x07
|
||||
@@ -90,36 +91,9 @@ enum
|
||||
EPOMAKER_OPTION_PEAK_TURN_CLOCKWISE = 0x10,
|
||||
};
|
||||
|
||||
class EpomakerController
|
||||
class EpomakerController : public RoyuanKeyboardController
|
||||
{
|
||||
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();
|
||||
};
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
@save :robot:
|
||||
@direct :x:
|
||||
@effects :white_check_mark:
|
||||
@detectors DetectEpomakerControllers
|
||||
@detectors DetectRoyuanLegacyKeyboardControllers
|
||||
@comment
|
||||
\*-------------------------------------------------------------------*/
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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();
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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();
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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);
|
||||
+4
@@ -22,6 +22,9 @@ 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;
|
||||
@@ -75,6 +78,7 @@ 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:
|
||||
|
||||
+1
@@ -20,6 +20,7 @@
|
||||
|
||||
#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,3 +150,18 @@ 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,6 +19,7 @@
|
||||
|
||||
#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 |
|
||||
@@ -58,8 +59,17 @@ 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:
|
||||
@@ -75,6 +85,14 @@ 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:
|
||||
|
||||
+20
@@ -65,6 +65,20 @@ 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();
|
||||
@@ -143,5 +157,11 @@ 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);
|
||||
}
|
||||
|
||||
+83
@@ -76,6 +76,15 @@ 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()
|
||||
@@ -265,6 +274,80 @@ 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)
|
||||
|
||||
+8
-1
@@ -30,10 +30,17 @@ 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;
|
||||
unsigned int direct_packet_length;
|
||||
bool brightness_supported;
|
||||
unsigned char current_brightness;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "SteelSeriesQCKMatController.h"
|
||||
#include "SteelSeriesRivalController.h"
|
||||
#include "SteelSeriesRival3Controller.h"
|
||||
#include "SteelSeriesRival5Controller.h"
|
||||
#include "SteelSeriesSenseiController.h"
|
||||
#include "SteelSeriesSiberiaController.h"
|
||||
#include "RGBController_SteelSeriesArctisNova3.h"
|
||||
@@ -32,6 +33,7 @@
|
||||
#include "RGBController_SteelSeriesQCKMat.h"
|
||||
#include "RGBController_SteelSeriesRival.h"
|
||||
#include "RGBController_SteelSeriesRival3.h"
|
||||
#include "RGBController_SteelSeriesRival5.h"
|
||||
#include "RGBController_SteelSeriesSensei.h"
|
||||
#include "RGBController_SteelSeriesSiberia.h"
|
||||
|
||||
@@ -83,6 +85,11 @@ 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;
|
||||
@@ -429,6 +436,32 @@ 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 |
|
||||
@@ -437,7 +470,8 @@ 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 Wired", DetectSteelSeriesAerox3, STEELSERIES_VID, STEELSERIES_AEROX_3_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 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 );
|
||||
@@ -471,6 +505,7 @@ 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,6 +22,7 @@
|
||||
#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
|
||||
@@ -48,6 +49,7 @@
|
||||
#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
|
||||
@@ -135,4 +137,6 @@ 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;
|
||||
|
||||
+3
-1
@@ -30,7 +30,9 @@ 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_DISCO = 0x06,
|
||||
STEELSERIES_MOUSE_EFFECT_REACTIVE = 0x07,
|
||||
STEELSERIES_MOUSE_EFFECT_RAINBOW = 0x08
|
||||
};
|
||||
|
||||
typedef struct
|
||||
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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 |
|
||||
\*--------------------------------------------------------*/
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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;
|
||||
};
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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));
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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;
|
||||
};
|
||||
@@ -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 |
|
||||
| 2 | unsigned short | zone_num_modes | 6 | Number of modes in zone |
|
||||
| 4 | int | zone_active_mode | 6 | Zone active_mode field value |
|
||||
| 2 | unsigned short | zone_num_modes | 6 | Number of modes in zone |
|
||||
| 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 |
|
||||
| 4 | unsigned int | bus_count | 6 | Number of I2C bus entries |
|
||||
| 2 | unsigned short | 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 |
|
||||
| 4 | unsigned int | device_count | 6 | Number of HID device entries |
|
||||
| 2 | unsigned short | 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 |
|
||||
| 4 | unsigned int | device_count | 6 | Number of USB device entries |
|
||||
| 2 | unsigned short | 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 |
|
||||
| 4 | unsigned int | port_count | 6 | Number of serial port entries |
|
||||
| 2 | unsigned short | 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 |
|
||||
| 4 | unsigned int | port_count | 6 | Number of USB serial port entries |
|
||||
| 2 | unsigned short | 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
|
||||
|
||||
+471
-180
@@ -96,13 +96,195 @@ 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();
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
@@ -247,6 +429,16 @@ 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 |
|
||||
\*-----------------------------------------------------*/
|
||||
@@ -271,6 +463,17 @@ 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 |
|
||||
\*-----------------------------------------------------*/
|
||||
@@ -323,6 +526,36 @@ 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;
|
||||
@@ -409,61 +642,59 @@ std::vector<HIDDeviceInfo> NetworkClient::GetHIDDeviceInfo()
|
||||
send_in_progress.unlock();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Wait for response |
|
||||
| Wait for response using the response queue |
|
||||
\*-------------------------------------------------*/
|
||||
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
|
||||
waiting_on_response_cv.wait(wait_lock);
|
||||
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_GET_HID_DEVICE_INFO);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Parse response into device list |
|
||||
\*-------------------------------------------------*/
|
||||
if(response_header.pkt_id == NET_PACKET_ID_GET_HID_DEVICE_INFO && response_data_ptr != NULL)
|
||||
if(response_entry.data != NULL)
|
||||
{
|
||||
unsigned int device_count = 0;
|
||||
unsigned char* data_ptr = response_data_ptr;
|
||||
unsigned int& data_size = response_header.pkt_size;
|
||||
unsigned short device_count = 0;
|
||||
unsigned char* data_ptr = response_entry.data;
|
||||
unsigned int& data_size = response_entry.header.pkt_size;
|
||||
unsigned int data_size_pkt;
|
||||
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, data_size_pkt);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, data_size_pkt);
|
||||
|
||||
if(data_size_pkt == data_size)
|
||||
{
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, device_count);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, device_count);
|
||||
|
||||
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
|
||||
for(unsigned short device_idx = 0; device_idx < device_count; device_idx++)
|
||||
{
|
||||
HIDDeviceInfo device;
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
unsigned short serial_number_size;
|
||||
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);
|
||||
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);
|
||||
|
||||
unsigned short manufacturer_string_size;
|
||||
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);
|
||||
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);
|
||||
|
||||
unsigned short product_string_size;
|
||||
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);
|
||||
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);
|
||||
|
||||
unsigned short path_size;
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, path_size);
|
||||
COPY_STRING_FIELD(data_ptr, response_data_ptr, path_size, device.path);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, path_size);
|
||||
COPY_STRING_FIELD(data_ptr, response_entry.data, path_size, device.path);
|
||||
|
||||
device_info.push_back(device);
|
||||
}
|
||||
}
|
||||
|
||||
COPY_DATA_ERROR:
|
||||
delete[] response_data_ptr;
|
||||
response_data_ptr = NULL;
|
||||
delete[] response_entry.data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,40 +718,38 @@ std::vector<i2c_smbus_info> NetworkClient::GetI2CBusInfo()
|
||||
send_in_progress.unlock();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Wait for response |
|
||||
| Wait for response using the response queue |
|
||||
\*-------------------------------------------------*/
|
||||
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
|
||||
waiting_on_response_cv.wait(wait_lock);
|
||||
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_GET_I2C_BUS_INFO);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Parse response into bus list |
|
||||
\*-------------------------------------------------*/
|
||||
if(response_header.pkt_id == NET_PACKET_ID_GET_I2C_BUS_INFO && response_data_ptr != NULL)
|
||||
if(response_entry.data != NULL)
|
||||
{
|
||||
unsigned int bus_count = 0;
|
||||
unsigned char* data_ptr = response_data_ptr;
|
||||
unsigned int& data_size = response_header.pkt_size;
|
||||
unsigned short bus_count = 0;
|
||||
unsigned char* data_ptr = response_entry.data;
|
||||
unsigned int& data_size = response_entry.header.pkt_size;
|
||||
unsigned int data_size_pkt;
|
||||
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, data_size_pkt);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, data_size_pkt);
|
||||
|
||||
if(data_size_pkt == data_size)
|
||||
{
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, bus_count);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, bus_count);
|
||||
|
||||
for(unsigned int bus_idx = 0; bus_idx < bus_count; bus_idx++)
|
||||
for(unsigned short bus_idx = 0; bus_idx < bus_count; bus_idx++)
|
||||
{
|
||||
i2c_smbus_info bus;
|
||||
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, bus);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, bus);
|
||||
|
||||
bus_info.push_back(bus);
|
||||
}
|
||||
}
|
||||
|
||||
COPY_DATA_ERROR:
|
||||
delete[] response_data_ptr;
|
||||
response_data_ptr = NULL;
|
||||
delete[] response_entry.data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,41 +773,39 @@ std::vector<std::string> NetworkClient::GetSerialPorts()
|
||||
send_in_progress.unlock();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Wait for response |
|
||||
| Wait for response using the response queue |
|
||||
\*-------------------------------------------------*/
|
||||
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
|
||||
waiting_on_response_cv.wait(wait_lock);
|
||||
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_GET_SERIAL_PORTS);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Parse response into device list |
|
||||
\*-------------------------------------------------*/
|
||||
if(response_header.pkt_id == NET_PACKET_ID_GET_SERIAL_PORTS && response_data_ptr != NULL)
|
||||
if(response_entry.data != NULL)
|
||||
{
|
||||
unsigned int port_count = 0;
|
||||
unsigned char* data_ptr = response_data_ptr;
|
||||
unsigned int& data_size = response_header.pkt_size;
|
||||
unsigned short port_count = 0;
|
||||
unsigned char* data_ptr = response_entry.data;
|
||||
unsigned int& data_size = response_entry.header.pkt_size;
|
||||
unsigned int data_size_pkt;
|
||||
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, data_size_pkt);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, data_size_pkt);
|
||||
|
||||
if(data_size_pkt == data_size)
|
||||
{
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, port_count);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, port_count);
|
||||
|
||||
for(unsigned int port_idx = 0; port_idx < port_count; port_idx++)
|
||||
for(unsigned short port_idx = 0; port_idx < port_count; port_idx++)
|
||||
{
|
||||
std::string port_string;
|
||||
unsigned short port_string_size;
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, port_string_size);
|
||||
COPY_STRING_FIELD(data_ptr, response_data_ptr, port_string_size, port_string);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, port_string_size);
|
||||
COPY_STRING_FIELD(data_ptr, response_entry.data, port_string_size, port_string);
|
||||
|
||||
serial_ports.push_back(port_string);
|
||||
}
|
||||
}
|
||||
|
||||
COPY_DATA_ERROR:
|
||||
delete[] response_data_ptr;
|
||||
response_data_ptr = NULL;
|
||||
delete[] response_entry.data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,53 +829,51 @@ std::vector<USBDeviceInfo> NetworkClient::GetUSBDeviceInfo()
|
||||
send_in_progress.unlock();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Wait for response |
|
||||
| Wait for response using the response queue |
|
||||
\*-------------------------------------------------*/
|
||||
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
|
||||
waiting_on_response_cv.wait(wait_lock);
|
||||
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_GET_USB_DEVICE_INFO);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Parse response into device list |
|
||||
\*-------------------------------------------------*/
|
||||
if(response_header.pkt_id == NET_PACKET_ID_GET_USB_DEVICE_INFO && response_data_ptr != NULL)
|
||||
if(response_entry.data != NULL)
|
||||
{
|
||||
unsigned int device_count = 0;
|
||||
unsigned char* data_ptr = response_data_ptr;
|
||||
unsigned int& data_size = response_header.pkt_size;
|
||||
unsigned short device_count = 0;
|
||||
unsigned char* data_ptr = response_entry.data;
|
||||
unsigned int& data_size = response_entry.header.pkt_size;
|
||||
unsigned int data_size_pkt;
|
||||
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, data_size_pkt);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, data_size_pkt);
|
||||
|
||||
if(data_size_pkt == data_size)
|
||||
{
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, device_count);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, device_count);
|
||||
|
||||
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
|
||||
for(unsigned short device_idx = 0; device_idx < device_count; device_idx++)
|
||||
{
|
||||
USBDeviceInfo device;
|
||||
|
||||
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_entry.data, device.vendor_id);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, device.product_id);
|
||||
|
||||
unsigned short serial_number_size;
|
||||
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);
|
||||
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);
|
||||
|
||||
unsigned short manufacturer_string_size;
|
||||
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);
|
||||
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);
|
||||
|
||||
unsigned short product_string_size;
|
||||
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);
|
||||
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);
|
||||
|
||||
device_info.push_back(device);
|
||||
}
|
||||
}
|
||||
|
||||
COPY_DATA_ERROR:
|
||||
delete[] response_data_ptr;
|
||||
response_data_ptr = NULL;
|
||||
delete[] response_entry.data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -672,49 +897,47 @@ std::vector<SerialDeviceInfo> NetworkClient::GetUSBSerialPorts()
|
||||
send_in_progress.unlock();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Wait for response |
|
||||
| Wait for response using the response queue |
|
||||
\*-------------------------------------------------*/
|
||||
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
|
||||
waiting_on_response_cv.wait(wait_lock);
|
||||
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_GET_USB_SERIAL_PORTS);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Parse response into device list |
|
||||
\*-------------------------------------------------*/
|
||||
if(response_header.pkt_id == NET_PACKET_ID_GET_USB_SERIAL_PORTS && response_data_ptr != NULL)
|
||||
if(response_entry.data != NULL)
|
||||
{
|
||||
unsigned int device_count = 0;
|
||||
unsigned char* data_ptr = response_data_ptr;
|
||||
unsigned int& data_size = response_header.pkt_size;
|
||||
unsigned short device_count = 0;
|
||||
unsigned char* data_ptr = response_entry.data;
|
||||
unsigned int& data_size = response_entry.header.pkt_size;
|
||||
unsigned int data_size_pkt;
|
||||
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, data_size_pkt);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, data_size_pkt);
|
||||
|
||||
if(data_size_pkt == data_size)
|
||||
{
|
||||
COPY_DATA_FIELD(data_ptr, response_data_ptr, device_count);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, device_count);
|
||||
|
||||
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
|
||||
for(unsigned short device_idx = 0; device_idx < device_count; device_idx++)
|
||||
{
|
||||
SerialDeviceInfo device;
|
||||
|
||||
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_entry.data, device.vendor_id);
|
||||
COPY_DATA_FIELD(data_ptr, response_entry.data, device.product_id);
|
||||
|
||||
unsigned short port_path_size;
|
||||
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);
|
||||
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);
|
||||
|
||||
unsigned short usb_path_size;
|
||||
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);
|
||||
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);
|
||||
|
||||
device_info.push_back(device);
|
||||
}
|
||||
}
|
||||
|
||||
COPY_DATA_ERROR:
|
||||
delete[] response_data_ptr;
|
||||
response_data_ptr = NULL;
|
||||
delete[] response_entry.data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -774,23 +997,21 @@ unsigned int NetworkClient::LogManager_GetLogLevel()
|
||||
send_in_progress.unlock();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Wait for response |
|
||||
| Wait for response using the response queue |
|
||||
\*-----------------------------------------------------*/
|
||||
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
|
||||
waiting_on_response_cv.wait(wait_lock);
|
||||
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_LOGMANAGER_GET_LOG_LEVEL);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Parse response into bus list |
|
||||
\*-----------------------------------------------------*/
|
||||
if(response_header.pkt_id == NET_PACKET_ID_LOGMANAGER_GET_LOG_LEVEL && response_data_ptr != NULL)
|
||||
if(response_entry.data != NULL)
|
||||
{
|
||||
if(response_header.pkt_size >= sizeof(log_level))
|
||||
if(response_entry.header.pkt_size >= sizeof(log_level))
|
||||
{
|
||||
memcpy(&log_level, response_data_ptr, sizeof(log_level));
|
||||
memcpy(&log_level, response_entry.data, sizeof(log_level));
|
||||
}
|
||||
|
||||
delete[] response_data_ptr;
|
||||
response_data_ptr = NULL;
|
||||
delete[] response_entry.data;
|
||||
}
|
||||
|
||||
return(log_level);
|
||||
@@ -886,24 +1107,22 @@ std::string NetworkClient::ProfileManager_DownloadProfile(std::string profile_na
|
||||
send_in_progress.unlock();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Wait for response |
|
||||
| Wait for response using the response queue |
|
||||
\*-----------------------------------------------------*/
|
||||
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
|
||||
waiting_on_response_cv.wait(wait_lock);
|
||||
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_PROFILEMANAGER_DOWNLOAD_PROFILE);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Parse response into bus list |
|
||||
\*-----------------------------------------------------*/
|
||||
if(response_header.pkt_id == NET_PACKET_ID_PROFILEMANAGER_DOWNLOAD_PROFILE && response_data_ptr != NULL)
|
||||
if(response_entry.data != NULL)
|
||||
{
|
||||
unsigned int& data_size = response_header.pkt_size;
|
||||
unsigned char* data_ptr = response_data_ptr;
|
||||
unsigned int& data_size = response_entry.header.pkt_size;
|
||||
unsigned char* data_ptr = response_entry.data;
|
||||
|
||||
COPY_STRING_FIELD(data_ptr, response_data_ptr, data_size, response_string);
|
||||
COPY_STRING_FIELD(data_ptr, response_entry.data, data_size, response_string);
|
||||
|
||||
COPY_DATA_ERROR:
|
||||
delete[] response_data_ptr;
|
||||
response_data_ptr = NULL;
|
||||
delete[] response_entry.data;
|
||||
}
|
||||
|
||||
return(response_string);
|
||||
@@ -924,24 +1143,22 @@ std::string NetworkClient::ProfileManager_GetActiveProfile()
|
||||
send_in_progress.unlock();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Wait for response |
|
||||
| Wait for response using the response queue |
|
||||
\*-----------------------------------------------------*/
|
||||
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
|
||||
waiting_on_response_cv.wait(wait_lock);
|
||||
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_PROFILEMANAGER_GET_ACTIVE_PROFILE);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Parse response into bus list |
|
||||
\*-----------------------------------------------------*/
|
||||
if(response_header.pkt_id == NET_PACKET_ID_PROFILEMANAGER_GET_ACTIVE_PROFILE && response_data_ptr != NULL)
|
||||
if(response_entry.data != NULL)
|
||||
{
|
||||
unsigned int& data_size = response_header.pkt_size;
|
||||
unsigned char* data_ptr = response_data_ptr;
|
||||
unsigned int& data_size = response_entry.header.pkt_size;
|
||||
unsigned char* data_ptr = response_entry.data;
|
||||
|
||||
COPY_STRING_FIELD(data_ptr, response_data_ptr, data_size, response_string);
|
||||
COPY_STRING_FIELD(data_ptr, response_entry.data, data_size, response_string);
|
||||
|
||||
COPY_DATA_ERROR:
|
||||
delete[] response_data_ptr;
|
||||
response_data_ptr = NULL;
|
||||
delete[] response_entry.data;
|
||||
}
|
||||
|
||||
return(response_string);
|
||||
@@ -977,23 +1194,22 @@ std::string NetworkClient::SettingsManager_GetSettings(std::string settings_key)
|
||||
send_in_progress.unlock();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Wait for response |
|
||||
| Wait for response using the response queue |
|
||||
\*-----------------------------------------------------*/
|
||||
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
|
||||
waiting_on_response_cv.wait(wait_lock);
|
||||
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_SETTINGSMANAGER_GET_SETTINGS);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Parse response into bus list |
|
||||
\*-----------------------------------------------------*/
|
||||
if(response_header.pkt_id == NET_PACKET_ID_SETTINGSMANAGER_GET_SETTINGS && response_data_ptr != NULL)
|
||||
if(response_entry.data != NULL)
|
||||
{
|
||||
unsigned int& data_size = response_header.pkt_size;
|
||||
unsigned char* data_ptr = response_data_ptr;
|
||||
unsigned int& data_size = response_entry.header.pkt_size;
|
||||
unsigned char* data_ptr = response_entry.data;
|
||||
|
||||
COPY_STRING_FIELD(data_ptr, response_data_ptr, data_size, response_string);
|
||||
COPY_STRING_FIELD(data_ptr, response_entry.data, data_size, response_string);
|
||||
|
||||
COPY_DATA_ERROR:
|
||||
response_data_ptr = NULL;
|
||||
delete[] response_entry.data;
|
||||
}
|
||||
|
||||
return(response_string);
|
||||
@@ -1015,24 +1231,22 @@ std::string NetworkClient::SettingsManager_GetSettingsSchema(std::string setting
|
||||
send_in_progress.unlock();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Wait for response |
|
||||
| Wait for response using the response queue |
|
||||
\*-----------------------------------------------------*/
|
||||
std::unique_lock<std::mutex> wait_lock(waiting_on_response_mutex);
|
||||
waiting_on_response_cv.wait(wait_lock);
|
||||
NetworkClientListenerThreadQueueEntry response_entry = WaitForResponse(NET_PACKET_ID_SETTINGSMANAGER_GET_SETTINGS_SCHEMA);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Parse response into bus list |
|
||||
\*-----------------------------------------------------*/
|
||||
if(response_header.pkt_id == NET_PACKET_ID_SETTINGSMANAGER_GET_SETTINGS_SCHEMA && response_data_ptr != NULL)
|
||||
if(response_entry.data != NULL)
|
||||
{
|
||||
unsigned int& data_size = response_header.pkt_size;
|
||||
unsigned char* data_ptr = response_data_ptr;
|
||||
unsigned int& data_size = response_entry.header.pkt_size;
|
||||
unsigned char* data_ptr = response_entry.data;
|
||||
|
||||
COPY_STRING_FIELD(data_ptr, response_data_ptr, data_size, response_string);
|
||||
COPY_STRING_FIELD(data_ptr, response_entry.data, data_size, response_string);
|
||||
|
||||
COPY_DATA_ERROR:
|
||||
delete[] response_data_ptr;
|
||||
response_data_ptr = NULL;
|
||||
delete[] response_entry.data;
|
||||
}
|
||||
|
||||
return(response_string);
|
||||
@@ -1723,6 +1937,16 @@ 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 |
|
||||
\*-----------------------------------------------------*/
|
||||
@@ -1806,46 +2030,30 @@ 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:
|
||||
@@ -1855,15 +2063,19 @@ 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::unique_lock<std::mutex> lock(waiting_on_response_mutex);
|
||||
std::lock_guard<std::mutex> lock(receive_queue_mutex);
|
||||
|
||||
response_header = header;
|
||||
response_data_ptr = data;
|
||||
delete_data = false;
|
||||
NetworkClientListenerThreadQueueEntry new_entry;
|
||||
new_entry.header = header;
|
||||
new_entry.data = data;
|
||||
new_entry.generation = listen_generation;
|
||||
|
||||
lock.unlock();
|
||||
waiting_on_response_cv.notify_all();
|
||||
receive_queue.push_back(new_entry);
|
||||
receive_queue_cv.notify_all();
|
||||
|
||||
delete_data = false;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1881,8 +2093,9 @@ void NetworkClient::ListenThreadFunction()
|
||||
profilemanager_thread->queue_mutex.lock();
|
||||
|
||||
NetworkClientListenerThreadQueueEntry new_entry;
|
||||
new_entry.data = data;
|
||||
new_entry.header = header;
|
||||
new_entry.data = data;
|
||||
new_entry.generation = listen_generation;
|
||||
|
||||
profilemanager_thread->queue.push(new_entry);
|
||||
profilemanager_thread->queue_mutex.unlock();
|
||||
@@ -1901,6 +2114,7 @@ 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;
|
||||
@@ -1918,23 +2132,39 @@ 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();
|
||||
|
||||
std::vector<RGBController *> server_controllers_copy = server_controllers;
|
||||
orphaned_controllers.insert(orphaned_controllers.end(), server_controllers.begin(), server_controllers.end());
|
||||
|
||||
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();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Client info has changed, call the callbacks |
|
||||
| Signal only an unplanned connection loss; on a |
|
||||
| requested stop the caller runs the teardown |
|
||||
\*-----------------------------------------------------*/
|
||||
SignalNetworkClientUpdate(NETWORKCLIENT_UPDATE_REASON_CLIENT_DISCONNECTED);
|
||||
if(client_active)
|
||||
{
|
||||
SignalNetworkClientUpdate(NETWORKCLIENT_UPDATE_REASON_CLIENT_DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
@@ -1958,7 +2188,11 @@ 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);
|
||||
RGBController::SetDeviceDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), new_controller, GetProtocolVersion());
|
||||
|
||||
if(RGBController::SetDeviceDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), new_controller, GetProtocolVersion()) == NULL)
|
||||
{
|
||||
goto COPY_DATA_ERROR;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Mark this controller as remote owned |
|
||||
@@ -2255,7 +2489,10 @@ void NetworkClient::ProcessRequest_RGBController_SignalUpdate(unsigned int data_
|
||||
| UpdateLEDs() sends color description |
|
||||
\*-------------------------------------------------*/
|
||||
case RGBCONTROLLER_UPDATE_REASON_UPDATELEDS:
|
||||
RGBController::SetColorDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), controller, GetProtocolVersion());
|
||||
if(RGBController::SetColorDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), controller, GetProtocolVersion()) == NULL)
|
||||
{
|
||||
goto COPY_DATA_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
@@ -2272,7 +2509,10 @@ void NetworkClient::ProcessRequest_RGBController_SignalUpdate(unsigned int data_
|
||||
case RGBCONTROLLER_UPDATE_REASON_SETDEVICESPECIFICCONFIGURATION:
|
||||
case RGBCONTROLLER_UPDATE_REASON_SETDEVICESPECIFICZONECONFIGURATION:
|
||||
default:
|
||||
RGBController::SetDeviceDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), controller, GetProtocolVersion());
|
||||
if(RGBController::SetDeviceDescription(data_ptr, data_size - (unsigned int)(data_ptr - data_start), controller, GetProtocolVersion()) == NULL)
|
||||
{
|
||||
goto COPY_DATA_ERROR;
|
||||
}
|
||||
|
||||
/*---------------------------------------------*\
|
||||
| Mark this controller as remote owned |
|
||||
@@ -2500,6 +2740,57 @@ 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 |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
+38
-4
@@ -12,6 +12,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
@@ -50,6 +51,7 @@ typedef struct
|
||||
{
|
||||
NetPacketHeader header;
|
||||
unsigned char* data;
|
||||
unsigned int generation;
|
||||
} NetworkClientListenerThreadQueueEntry;
|
||||
|
||||
typedef struct
|
||||
@@ -99,6 +101,12 @@ 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();
|
||||
|
||||
@@ -195,10 +203,17 @@ private:
|
||||
unsigned int requested_controller_index;
|
||||
std::mutex send_in_progress;
|
||||
|
||||
NetPacketHeader response_header;
|
||||
unsigned char* response_data_ptr;
|
||||
std::mutex waiting_on_response_mutex;
|
||||
std::condition_variable waiting_on_response_cv;
|
||||
/*-----------------------------------------------------*\
|
||||
| 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;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Client information |
|
||||
@@ -233,6 +248,18 @@ 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 |
|
||||
\*-----------------------------------------------------*/
|
||||
@@ -245,6 +272,7 @@ private:
|
||||
\*-----------------------------------------------------*/
|
||||
std::mutex ControllerListMutex;
|
||||
std::vector<RGBController*> server_controllers;
|
||||
std::vector<RGBController*> orphaned_controllers;
|
||||
std::vector<unsigned int> server_controller_ids;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
@@ -296,6 +324,12 @@ 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 |
|
||||
\*-----------------------------------------------------*/
|
||||
|
||||
+177
-29
@@ -97,6 +97,8 @@ 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()
|
||||
@@ -104,8 +106,39 @@ 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);
|
||||
}
|
||||
}
|
||||
@@ -141,6 +174,7 @@ NetworkServer::NetworkServer()
|
||||
|
||||
plugin_manager = nullptr;
|
||||
profile_manager = nullptr;
|
||||
profilemanager_thread = nullptr;
|
||||
settings_manager = nullptr;
|
||||
}
|
||||
|
||||
@@ -997,6 +1031,14 @@ 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();
|
||||
|
||||
@@ -1068,7 +1110,7 @@ void NetworkServer::ControllerListenThread(NetworkServerControllerThread* this_t
|
||||
|
||||
delete[] queue_entry.data;
|
||||
|
||||
SendAck(queue_entry.client_info, queue_entry.header.pkt_id, queue_entry.header.pkt_id, status);
|
||||
SendAck(queue_entry.client_info, queue_entry.header.pkt_dev_id, queue_entry.header.pkt_id, status);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1136,7 +1178,7 @@ void NetworkServer::ProfileManagerListenThread(NetworkServerControllerThread* th
|
||||
|
||||
delete[] queue_entry.data;
|
||||
|
||||
SendAck(queue_entry.client_info, queue_entry.header.pkt_id, queue_entry.header.pkt_id, status);
|
||||
SendAck(queue_entry.client_info, queue_entry.header.pkt_dev_id, queue_entry.header.pkt_id, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1646,7 +1688,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetHIDDeviceInfo(NetworkClientInfo
|
||||
std::vector<HIDDeviceInfo> device_info = ResourceManager::get()->GetHIDDeviceInfo();
|
||||
|
||||
unsigned int data_size = 0;
|
||||
unsigned int device_count = (unsigned int)device_info.size();
|
||||
unsigned short device_count = (unsigned short)device_info.size();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Calculate data size |
|
||||
@@ -1654,7 +1696,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetHIDDeviceInfo(NetworkClientInfo
|
||||
data_size += sizeof(data_size);
|
||||
data_size += sizeof(device_count);
|
||||
|
||||
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
|
||||
for(unsigned short 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);
|
||||
@@ -1754,7 +1796,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetI2CBusInfo(NetworkClientInfo* c
|
||||
std::vector<i2c_smbus_info> bus_info = ResourceManager::get()->GetI2CBusInfo();
|
||||
|
||||
unsigned int data_size = 0;
|
||||
unsigned int bus_count = (unsigned int)bus_info.size();
|
||||
unsigned short bus_count = (unsigned short)bus_info.size();
|
||||
data_size += sizeof(data_size);
|
||||
data_size += sizeof(bus_count);
|
||||
data_size += bus_count * sizeof(i2c_smbus_info);
|
||||
@@ -1768,7 +1810,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetI2CBusInfo(NetworkClientInfo* c
|
||||
memcpy(data_ptr, &bus_count, sizeof(bus_count));
|
||||
data_ptr += sizeof(bus_count);
|
||||
|
||||
for(std::size_t bus_idx = 0; bus_idx < bus_info.size(); bus_idx++)
|
||||
for(unsigned short bus_idx = 0; bus_idx < bus_count; bus_idx++)
|
||||
{
|
||||
memcpy(data_ptr, &bus_info[bus_idx], sizeof(bus_info[bus_idx]));
|
||||
data_ptr += sizeof(bus_info[bus_idx]);
|
||||
@@ -1799,7 +1841,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetSerialPorts(NetworkClientInfo*
|
||||
std::vector<std::string> serial_ports = ResourceManager::get()->GetSerialPorts();
|
||||
|
||||
unsigned int data_size = 0;
|
||||
unsigned int port_count = (unsigned int)serial_ports.size();
|
||||
unsigned short port_count = (unsigned short)serial_ports.size();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Calculate data size |
|
||||
@@ -1807,7 +1849,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetSerialPorts(NetworkClientInfo*
|
||||
data_size += sizeof(data_size);
|
||||
data_size += sizeof(port_count);
|
||||
|
||||
for(unsigned int port_idx = 0; port_idx < port_count; port_idx++)
|
||||
for(unsigned short 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;
|
||||
@@ -1825,7 +1867,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetSerialPorts(NetworkClientInfo*
|
||||
memcpy(data_ptr, &port_count, sizeof(port_count));
|
||||
data_ptr += sizeof(port_count);
|
||||
|
||||
for(unsigned int port_idx = 0; port_idx < port_count; port_idx++)
|
||||
for(unsigned short 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));
|
||||
@@ -1859,7 +1901,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetUSBDeviceInfo(NetworkClientInfo
|
||||
std::vector<USBDeviceInfo> device_info = ResourceManager::get()->GetUSBDeviceInfo();
|
||||
|
||||
unsigned int data_size = 0;
|
||||
unsigned int device_count = (unsigned int)device_info.size();
|
||||
unsigned short device_count = (unsigned short)device_info.size();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Calculate data size |
|
||||
@@ -1867,7 +1909,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetUSBDeviceInfo(NetworkClientInfo
|
||||
data_size += sizeof(data_size);
|
||||
data_size += sizeof(device_count);
|
||||
|
||||
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
|
||||
for(unsigned short 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);
|
||||
@@ -1943,7 +1985,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetUSBSerialPorts(NetworkClientInf
|
||||
std::vector<SerialDeviceInfo> device_info = ResourceManager::get()->GetUSBSerialPorts();
|
||||
|
||||
unsigned int data_size = 0;
|
||||
unsigned int device_count = (unsigned int)device_info.size();
|
||||
unsigned short device_count = (unsigned short)device_info.size();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Calculate data size |
|
||||
@@ -1951,7 +1993,7 @@ NetPacketStatus NetworkServer::ProcessRequest_GetUSBSerialPorts(NetworkClientInf
|
||||
data_size += sizeof(data_size);
|
||||
data_size += sizeof(device_count);
|
||||
|
||||
for(unsigned int device_idx = 0; device_idx < device_count; device_idx++)
|
||||
for(unsigned short 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);
|
||||
@@ -2986,7 +3028,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);
|
||||
}
|
||||
@@ -4043,15 +4085,19 @@ void NetworkServer::SendRequest_RGBController_SignalUpdate(RGBController * contr
|
||||
if(found)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Send to each connected client with protocol 6 or |
|
||||
| higher |
|
||||
| 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. |
|
||||
\*-------------------------------------------------*/
|
||||
ServerClientsMutex.lock();
|
||||
|
||||
for(std::size_t client_idx = 0; client_idx < ServerClients.size(); client_idx++)
|
||||
{
|
||||
if(ServerClients[client_idx]->client_protocol_version >= 6)
|
||||
NetworkClientInfo* client_info = ServerClients[client_idx];
|
||||
|
||||
if(client_info->client_protocol_version >= 6)
|
||||
{
|
||||
SOCKET client_sock = ServerClients[client_idx]->client_sock;
|
||||
unsigned int protocol_version = ServerClients[client_idx]->client_protocol_version;
|
||||
unsigned int protocol_version = client_info->client_protocol_version;
|
||||
NetPacketHeader reply_hdr;
|
||||
unsigned char* reply_data;
|
||||
unsigned int reply_size;
|
||||
@@ -4154,19 +4200,121 @@ void NetworkServer::SendRequest_RGBController_SignalUpdate(RGBController * contr
|
||||
InitNetPacketHeader(&reply_hdr, controller_id, NET_PACKET_ID_RGBCONTROLLER_SIGNALUPDATE, reply_size);
|
||||
|
||||
/*-----------------------------------------*\
|
||||
| Send packet |
|
||||
| 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_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();
|
||||
{
|
||||
std::lock_guard<std::mutex> queue_lock(client_info->client_send_mutex);
|
||||
|
||||
/*-----------------------------------------*\
|
||||
| Delete data |
|
||||
\*-----------------------------------------*/
|
||||
delete[] reply_data;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,12 @@
|
||||
#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"
|
||||
@@ -29,8 +32,24 @@
|
||||
#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:
|
||||
@@ -45,6 +64,20 @@ 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
|
||||
@@ -220,6 +253,7 @@ 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);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "OpenRGBPluginAPI.h"
|
||||
#include "RGBController_Dummy.h"
|
||||
#include "RGBController_Virtual.h"
|
||||
|
||||
OpenRGBPluginAPI::OpenRGBPluginAPI()
|
||||
@@ -185,6 +186,11 @@ 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 |
|
||||
\*---------------------------------------------------------*/
|
||||
@@ -223,6 +229,87 @@ 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 |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
@@ -45,6 +45,7 @@ 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 |
|
||||
@@ -57,6 +58,27 @@ 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 |
|
||||
\*-----------------------------------------------------*/
|
||||
|
||||
+43
-21
@@ -77,43 +77,65 @@ 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 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;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| 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;
|
||||
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;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| 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
|
||||
|
||||
+25
-20
@@ -170,6 +170,7 @@ 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");
|
||||
@@ -183,7 +184,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.generic_u8string())
|
||||
if(plugin_settings["plugins_remove"][plugin_remove_idx] == path_string)
|
||||
{
|
||||
/*-----------------------------------------*\
|
||||
| Delete the plugin file |
|
||||
@@ -209,7 +210,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.c_str());
|
||||
LOG_WARNING("[%s] Plugin file %s already in list, skipping.", PLUGINMANAGER, path_string.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -217,7 +218,6 @@ 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.c_str());
|
||||
LOG_WARNING("[%s] Plugin %s does not have a MetaData field, skipping.", PLUGINMANAGER, path_string.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.c_str());
|
||||
LOG_WARNING("[%s] Plugin %s does not have an OpenRGBPluginAPIVersion field, skipping.", PLUGINMANAGER, path_string.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.c_str());
|
||||
LOG_WARNING("[%s] Plugin %s does not have an Id field, skipping.", PLUGINMANAGER, path_string.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -291,19 +291,29 @@ 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 = (plugin_api_version == OPENRGB_PLUGIN_API_VERSION);
|
||||
entry.incompatible = true;
|
||||
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)
|
||||
if(plugin_id == ActivePlugins[plugin_idx].id && !ActivePlugins[plugin_idx].incompatible)
|
||||
{
|
||||
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());
|
||||
@@ -311,23 +321,18 @@ 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.c_str(), loader->errorString().toStdString().c_str());
|
||||
LOG_WARNING("[%s] Plugin %s cannot be loaded: %s", PLUGINMANAGER, path_string.c_str(), loader->errorString().toStdString().c_str());
|
||||
goto add_plugin_entry;
|
||||
}
|
||||
|
||||
if(!instance)
|
||||
{
|
||||
LOG_WARNING("[%s] Plugin %s cannot be instantiated.", PLUGINMANAGER, path.c_str());
|
||||
LOG_WARNING("[%s] Plugin %s cannot be instantiated.", PLUGINMANAGER, path_string.c_str());
|
||||
goto add_plugin_entry;
|
||||
}
|
||||
|
||||
@@ -338,13 +343,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.c_str());
|
||||
LOG_WARNING("[%s] Plugin %s cannot be casted to OpenRGBPluginInterface", PLUGINMANAGER, path_string.c_str());
|
||||
goto add_plugin_entry;
|
||||
}
|
||||
|
||||
if(plugin->GetPluginAPIVersion() != OPENRGB_PLUGIN_API_VERSION)
|
||||
{
|
||||
LOG_WARNING("[%s] Plugin %s has a compatible API version", PLUGINMANAGER, path.c_str());
|
||||
LOG_WARNING("[%s] Plugin %s has an incompatible API version", PLUGINMANAGER, path_string.c_str());
|
||||
goto add_plugin_entry;
|
||||
}
|
||||
|
||||
@@ -433,7 +438,7 @@ void PluginManager::RemovePlugin(const filesystem::path& path)
|
||||
{
|
||||
unsigned int plugin_idx;
|
||||
|
||||
LOG_TRACE("[%s] Attempting to remove plugin %s", PLUGINMANAGER, path.c_str());
|
||||
LOG_TRACE("[%s] Attempting to remove plugin %s", PLUGINMANAGER, path.generic_u8string().c_str());
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Search active plugins to see if this path already |
|
||||
@@ -453,7 +458,7 @@ void PluginManager::RemovePlugin(const filesystem::path& path)
|
||||
\*-----------------------------------------------------*/
|
||||
if(plugin_idx == ActivePlugins.size())
|
||||
{
|
||||
LOG_TRACE("[%s] Plugin %s not active", PLUGINMANAGER, path.c_str());
|
||||
LOG_TRACE("[%s] Plugin %s not active", PLUGINMANAGER, path.generic_u8string().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -463,7 +468,7 @@ void PluginManager::RemovePlugin(const filesystem::path& path)
|
||||
\*-----------------------------------------------------*/
|
||||
if(ActivePlugins[plugin_idx].loader->isLoaded())
|
||||
{
|
||||
LOG_TRACE("[%s] Plugin %s is active, unloading", PLUGINMANAGER, path.c_str());
|
||||
LOG_TRACE("[%s] Plugin %s is active, unloading", PLUGINMANAGER, path.generic_u8string().c_str());
|
||||
UnloadPlugin(&ActivePlugins[plugin_idx]);
|
||||
}
|
||||
|
||||
|
||||
+224
-1
@@ -176,6 +176,106 @@ 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;
|
||||
@@ -681,7 +781,44 @@ bool ProfileManager::SaveProfileFromJSON(nlohmann::json profile_json)
|
||||
\*-------------------------------------------------*/
|
||||
UpdateProfileList();
|
||||
|
||||
SetActiveProfile(profile_json["profile_name"]);
|
||||
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);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
@@ -832,6 +969,92 @@ 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);
|
||||
|
||||
@@ -83,6 +83,7 @@ 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();
|
||||
@@ -110,6 +111,8 @@ public:
|
||||
void OnProfileAboutToLoad();
|
||||
void OnProfileLoaded(std::string profile_json_string);
|
||||
|
||||
void MigrateLegacyProfiles();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Callback Registration Functions |
|
||||
\*-----------------------------------------------------*/
|
||||
@@ -122,6 +125,7 @@ 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);
|
||||
|
||||
@@ -1075,7 +1075,8 @@ 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_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].direction = direction;
|
||||
}
|
||||
@@ -1408,7 +1409,8 @@ 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_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].direction = direction;
|
||||
}
|
||||
@@ -1857,17 +1859,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()
|
||||
@@ -1882,17 +1884,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)
|
||||
@@ -1913,14 +1915,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 |
|
||||
@@ -1929,7 +1931,7 @@ void RGBController::SignalUpdate(unsigned int update_reason)
|
||||
callbacks = UpdateCallbacks;
|
||||
callback_args = UpdateCallbackArgs;
|
||||
|
||||
UpdateMutex.unlock();
|
||||
//UpdateMutex.unlock();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Invoke the copied callbacks |
|
||||
@@ -1939,20 +1941,21 @@ 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();
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
@@ -2882,9 +2885,15 @@ unsigned char * RGBController::GetModeDescriptionData(unsigned char* data_ptr, m
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Copy in mode flags (data) |
|
||||
| Strip diagonal flag for SDK v5 and earlier clients |
|
||||
\*-----------------------------------------------------*/
|
||||
memcpy(data_ptr, &mode.flags, sizeof(mode.flags));
|
||||
data_ptr += sizeof(mode.flags);
|
||||
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);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Copy in mode speed_min (data) |
|
||||
@@ -2941,9 +2950,15 @@ unsigned char * RGBController::GetModeDescriptionData(unsigned char* data_ptr, m
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Copy in mode direction (data) |
|
||||
| Clamp diagonal directions for SDK v5 and earlier |
|
||||
\*-----------------------------------------------------*/
|
||||
memcpy(data_ptr, &mode.direction, sizeof(mode.direction));
|
||||
data_ptr += sizeof(mode.direction);
|
||||
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);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Copy in mode color_mode (data) |
|
||||
@@ -3174,6 +3189,12 @@ 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 |
|
||||
\*-------------------------------------------------*/
|
||||
@@ -3182,12 +3203,6 @@ 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 |
|
||||
\*-------------------------------------------------*/
|
||||
@@ -3261,8 +3276,8 @@ unsigned int RGBController::GetZoneDescriptionSize(zone zone, unsigned int proto
|
||||
{
|
||||
unsigned short num_modes = (unsigned short)zone.modes.size();
|
||||
|
||||
data_size += sizeof(num_modes);
|
||||
data_size += sizeof(zone.active_mode);
|
||||
data_size += sizeof(num_modes);
|
||||
|
||||
for(int mode_index = 0; mode_index < num_modes; mode_index++)
|
||||
{
|
||||
@@ -3889,17 +3904,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 |
|
||||
\*-------------------------------------------------*/
|
||||
|
||||
@@ -73,6 +73,7 @@ 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 */
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
@@ -86,6 +87,10 @@ 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 */
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
|
||||
@@ -99,7 +99,9 @@ void RGBController_Virtual::DeviceConfigureZone(int zone_idx)
|
||||
{
|
||||
if(DeviceConfigureZone_ptr)
|
||||
{
|
||||
AccessMutex.unlock();
|
||||
DeviceConfigureZone_ptr(object_ptr, zone_idx);
|
||||
AccessMutex.lock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +109,9 @@ void RGBController_Virtual::DeviceUpdateLEDs()
|
||||
{
|
||||
if(DeviceUpdateLEDs_ptr)
|
||||
{
|
||||
AccessMutex.unlock_shared();
|
||||
DeviceUpdateLEDs_ptr(object_ptr);
|
||||
AccessMutex.lock_shared();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +119,9 @@ void RGBController_Virtual::DeviceUpdateZoneLEDs(int zone)
|
||||
{
|
||||
if(DeviceUpdateZoneLEDs_ptr)
|
||||
{
|
||||
AccessMutex.unlock_shared();
|
||||
DeviceUpdateZoneLEDs_ptr(object_ptr, zone);
|
||||
AccessMutex.lock_shared();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +129,9 @@ void RGBController_Virtual::DeviceUpdateSingleLED(int led)
|
||||
{
|
||||
if(DeviceUpdateSingleLED_ptr)
|
||||
{
|
||||
AccessMutex.unlock_shared();
|
||||
DeviceUpdateSingleLED_ptr(object_ptr, led);
|
||||
AccessMutex.lock_shared();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +139,9 @@ void RGBController_Virtual::DeviceUpdateMode()
|
||||
{
|
||||
if(DeviceUpdateMode_ptr)
|
||||
{
|
||||
AccessMutex.unlock_shared();
|
||||
DeviceUpdateMode_ptr(object_ptr);
|
||||
AccessMutex.lock_shared();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +149,9 @@ void RGBController_Virtual::DeviceUpdateZoneMode(int zone)
|
||||
{
|
||||
if(DeviceUpdateZoneMode_ptr)
|
||||
{
|
||||
AccessMutex.unlock_shared();
|
||||
DeviceUpdateZoneMode_ptr(object_ptr, zone);
|
||||
AccessMutex.lock_shared();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +159,9 @@ void RGBController_Virtual::DeviceSaveMode()
|
||||
{
|
||||
if(DeviceSaveMode_ptr)
|
||||
{
|
||||
AccessMutex.unlock_shared();
|
||||
DeviceSaveMode_ptr(object_ptr);
|
||||
AccessMutex.lock_shared();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +169,9 @@ void RGBController_Virtual::DeviceUpdateDeviceSpecificConfiguration()
|
||||
{
|
||||
if(DeviceUpdateDeviceSpecificConfiguration_ptr)
|
||||
{
|
||||
AccessMutex.unlock();
|
||||
DeviceUpdateDeviceSpecificConfiguration_ptr(object_ptr);
|
||||
AccessMutex.lock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,6 +179,8 @@ void RGBController_Virtual::DeviceUpdateDeviceSpecificZoneConfiguration(int zone
|
||||
{
|
||||
if(DeviceUpdateDeviceSpecificZoneConfiguration_ptr)
|
||||
{
|
||||
AccessMutex.unlock();
|
||||
DeviceUpdateDeviceSpecificZoneConfiguration_ptr(object_ptr, zone);
|
||||
AccessMutex.lock();
|
||||
}
|
||||
}
|
||||
|
||||
+184
-22
@@ -86,6 +86,19 @@ 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;
|
||||
@@ -151,6 +164,13 @@ ResourceManager::ResourceManager()
|
||||
plugin_manager = NULL;
|
||||
server = NULL;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Start the client teardown thread |
|
||||
\*-----------------------------------------------------*/
|
||||
ClientTeardownPending = false;
|
||||
ClientTeardownRunning = true;
|
||||
ClientTeardownThread = new std::thread(&ResourceManager::ClientTeardownThreadFunction, this);
|
||||
|
||||
SetupConfigurationDirectory();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
@@ -274,7 +294,21 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
@@ -535,38 +569,155 @@ 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)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Stop the disconnecting 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 |
|
||||
\*-----------------------------------------------------*/
|
||||
network_client->StopClient();
|
||||
ClientTeardownMutex.lock();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| 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())
|
||||
if(std::find(clients_to_remove.begin(), clients_to_remove.end(), network_client) == clients_to_remove.end())
|
||||
{
|
||||
clients.erase(client_it);
|
||||
clients_to_remove.push_back(network_client);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Delete the client |
|
||||
\*-----------------------------------------------------*/
|
||||
delete network_client;
|
||||
ClientTeardownPending = true;
|
||||
|
||||
UpdateDeviceList();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
@@ -742,6 +893,8 @@ 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();
|
||||
@@ -752,6 +905,8 @@ void ResourceManager::UpdateDeviceList()
|
||||
}
|
||||
}
|
||||
|
||||
ClientListMutex.unlock();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Update server list |
|
||||
\*-----------------------------------------------------*/
|
||||
@@ -955,9 +1110,9 @@ void ResourceManager::Initialize(bool tryConnect, bool detectDevices, bool start
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Cache the parameters |
|
||||
| TODO: Possibly cache them in the CLI file somewhere |
|
||||
| Don't try auto connect if starting server |
|
||||
\*-----------------------------------------------------*/
|
||||
tryAutoConnect = tryConnect;
|
||||
tryAutoConnect = tryConnect && !startServer;
|
||||
detection_enabled = detectDevices;
|
||||
start_server = startServer;
|
||||
apply_post_options = applyPostOptions;
|
||||
@@ -988,6 +1143,13 @@ 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 |
|
||||
\*-----------------------------------------------------*/
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
@@ -102,6 +104,7 @@ public:
|
||||
\*-----------------------------------------------------*/
|
||||
void RegisterNetworkClient(NetworkClient* new_client);
|
||||
void UnregisterNetworkClient(NetworkClient* network_client);
|
||||
void QueueClientTeardown();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Local Client Accessors |
|
||||
@@ -139,6 +142,7 @@ public:
|
||||
|
||||
private:
|
||||
bool AttemptLocalConnection();
|
||||
void ClientTeardownThreadFunction();
|
||||
void SetupConfigurationDirectory();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
@@ -214,6 +218,23 @@ 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 |
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
#define SD_RECEIVE SHUT_RD
|
||||
#define SD_BOTH SHUT_RDWR
|
||||
#endif
|
||||
|
||||
//Network Port Class
|
||||
|
||||
@@ -359,6 +359,7 @@
|
||||
#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
|
||||
@@ -439,6 +440,7 @@
|
||||
#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
|
||||
|
||||
@@ -1094,18 +1094,21 @@ 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 );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
@@ -1113,38 +1116,29 @@ void OpenRGBDevicePage::UpdateMode()
|
||||
\*-----------------------------------------------------*/
|
||||
if(ui->DirectionBox->isEnabled())
|
||||
{
|
||||
if(supports_dir_hv)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
int offset = 0;
|
||||
|
||||
if(supports_dir_ud)
|
||||
if(supports_dir_lr && current_dir_idx < offset + 2)
|
||||
{
|
||||
if(supports_dir_lr)
|
||||
{
|
||||
current_direction = current_dir_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_direction = current_dir_idx + 2;
|
||||
}
|
||||
current_direction = current_dir_idx - offset;
|
||||
}
|
||||
if(supports_dir_lr) offset += 2;
|
||||
|
||||
if((supports_dir_lr)
|
||||
&&(current_dir_idx < 2))
|
||||
if(supports_dir_ud && current_dir_idx >= offset && current_dir_idx < offset + 2)
|
||||
{
|
||||
current_direction = current_dir_idx;
|
||||
current_direction = MODE_DIRECTION_UP + (current_dir_idx - offset);
|
||||
}
|
||||
if(supports_dir_ud) offset += 2;
|
||||
|
||||
if(supports_dir_hv && current_dir_idx >= offset && current_dir_idx < offset + 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);
|
||||
}
|
||||
|
||||
if(selected_zone_mode)
|
||||
@@ -1446,6 +1440,7 @@ 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;
|
||||
@@ -1539,7 +1534,15 @@ void OpenRGBDevicePage::UpdateModeUi()
|
||||
ui->DirectionBox->addItem(tr("Vertical"));
|
||||
}
|
||||
|
||||
if(supports_dir_lr || supports_dir_ud || supports_dir_hv)
|
||||
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)
|
||||
&&((direction == MODE_DIRECTION_LEFT)
|
||||
@@ -1580,6 +1583,19 @@ 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
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <QCheckBox>
|
||||
#include <QScreen>
|
||||
#include <QSpinBox>
|
||||
#include <QThread>
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
@@ -106,7 +107,14 @@ static void OpenRGBDialogResourceManagerCallback(void * this_ptr, unsigned int u
|
||||
break;
|
||||
|
||||
case RESOURCEMANAGER_UPDATE_REASON_DEVICE_LIST_UPDATED:
|
||||
QMetaObject::invokeMethod(this_obj, "onDeviceListUpdated", Qt::BlockingQueuedConnection);
|
||||
if(QThread::currentThread() != qApp->thread())
|
||||
{
|
||||
QMetaObject::invokeMethod(this_obj, "onDeviceListUpdated", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMetaObject::invokeMethod(this_obj, "onDeviceListUpdated", Qt::DirectConnection);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -960,6 +968,84 @@ void OpenRGBDialog::ClearDevicesList()
|
||||
}
|
||||
}
|
||||
|
||||
void OpenRGBDialog::ShowHideDevicesList()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Loop through all controller tabs and move any hidden |
|
||||
| controller's page to the hidden pages vector |
|
||||
\*-----------------------------------------------------*/
|
||||
for(int tab_idx = 0; tab_idx < ui->DevicesTabBar->count(); tab_idx++)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Get a pointer to the page at this index in the |
|
||||
| tab bar |
|
||||
\*-------------------------------------------------*/
|
||||
QWidget* page = ui->DevicesTabBar->widget(tab_idx);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Verify this page is an OpenRGBDevicePage |
|
||||
\*-------------------------------------------------*/
|
||||
if(dynamic_cast<OpenRGBDevicePage*>(page) != nullptr)
|
||||
{
|
||||
/*---------------------------------------------*\
|
||||
| If the controller for this page is hidden, |
|
||||
| remove the page from the tab bar and store it |
|
||||
| in the hidden pages vector |
|
||||
\*---------------------------------------------*/
|
||||
if(((OpenRGBDevicePage*)page)->GetController()->GetHidden())
|
||||
{
|
||||
hidden_pages.push_back((OpenRGBDevicePage*)page);
|
||||
ui->DevicesTabBar->removeTab(tab_idx);
|
||||
|
||||
/*-----------------------------------------*\
|
||||
| Decrement tab index to account for |
|
||||
| removing tab |
|
||||
\*-----------------------------------------*/
|
||||
tab_idx--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Loop through all pages in the hidden pages vector and |
|
||||
| move any no-longer-hidden controller's page back to |
|
||||
| the tab bar |
|
||||
\*-----------------------------------------------------*/
|
||||
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];
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| 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()))
|
||||
{
|
||||
ui->DevicesTabBar->addTab(page, "");
|
||||
|
||||
/*---------------------------------------------*\
|
||||
| Create the tab label |
|
||||
\*---------------------------------------------*/
|
||||
TabLabel* NewTabLabel = new TabLabel(OpenRGBFont::GetIconIDFromDeviceType(page->GetController()->GetDeviceType()), (char *)page->GetController()->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 |
|
||||
\*---------------------------------------------*/
|
||||
page_idx--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenRGBDialog::UpdateDevicesList()
|
||||
{
|
||||
std::vector<RGBController *> controllers = ResourceManager::get()->GetRGBControllers();
|
||||
@@ -1038,8 +1124,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;
|
||||
}
|
||||
@@ -1140,7 +1226,7 @@ void OpenRGBDialog::UpdateDevicesList()
|
||||
connect(NewPage,
|
||||
SIGNAL(RefreshList()),
|
||||
this,
|
||||
SLOT(onDeviceListUpdated()));
|
||||
SLOT(onDeviceListShowHide()));
|
||||
|
||||
if(controllers[controller_idx]->GetHidden())
|
||||
{
|
||||
@@ -1278,6 +1364,7 @@ void OpenRGBDialog::UpdateDevicesList()
|
||||
}
|
||||
|
||||
bool found = true;
|
||||
base_tab = controllers.size();
|
||||
|
||||
while(found)
|
||||
{
|
||||
@@ -1470,6 +1557,11 @@ void OpenRGBDialog::on_QuickWhite()
|
||||
on_SetAllDevices(0xFF, 0xFF, 0xFF);
|
||||
}
|
||||
|
||||
void OpenRGBDialog::onDeviceListShowHide()
|
||||
{
|
||||
ShowHideDevicesList();
|
||||
}
|
||||
|
||||
void OpenRGBDialog::onDeviceListUpdated()
|
||||
{
|
||||
UpdateDevicesList();
|
||||
@@ -1512,6 +1604,14 @@ 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 |
|
||||
\*-----------------------------------------------------*/
|
||||
|
||||
@@ -148,6 +148,7 @@ private:
|
||||
|
||||
void ClearDevicesList();
|
||||
void UpdateDevicesList();
|
||||
void ShowHideDevicesList();
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
bool SelectConfigProfile(const std::string name);
|
||||
|
||||
@@ -176,6 +177,7 @@ private slots:
|
||||
void on_QuickBlue();
|
||||
void on_QuickMagenta();
|
||||
void on_QuickWhite();
|
||||
void onDeviceListShowHide();
|
||||
void onDeviceListUpdated();
|
||||
void onDetectionProgressUpdated();
|
||||
void onDetectionStarted();
|
||||
|
||||
@@ -8,5 +8,15 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user