Compare commits

..
422 changed files with 30063 additions and 43805 deletions
+19 -55
View File
@@ -14,9 +14,9 @@
# run only if manually started to save CI minutes if #
# GitLab hosted runners #
# * For commits to the CalcProgrammer1/OpenRGB repository, #
# automatically run all jobs on the default branch or #
# tag builds, otherwise run them if the source is part #
# of a merge request #
# automatically run all jobs on the default branch, #
# otherwise run them if the source is part of a merge #
# request #
#-----------------------------------------------------------#
.downstream_rules:
rules:
@@ -26,7 +26,7 @@
.upstream_rules:
rules:
- if: '$CI_PROJECT_PATH == "CalcProgrammer1/OpenRGB" && ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG || $CI_PIPELINE_SOURCE == "merge_request_event")'
- if: '$CI_PROJECT_PATH == "CalcProgrammer1/OpenRGB" && ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_PIPELINE_SOURCE == "merge_request_event")'
when: on_success
- !reference [.downstream_rules, rules]
@@ -51,7 +51,7 @@ before_script:
# Supported Devices Build Target #
#-----------------------------------------------------------#
"Supported Devices":
image: registry.gitlab.com/openrgbdevelopers/openrgb-linux-ci-deb-builder:trixie-amd64
image: registry.gitlab.com/openrgbdevelopers/openrgb-linux-ci-deb-builder:bookworm-amd64
tags:
- linux
- amd64
@@ -71,10 +71,10 @@ before_script:
- !reference [.upstream_rules, rules]
#-----------------------------------------------------------#
# OpenRGB Common AppImage Build Steps #
# OpenRGB Common Appimage Build Steps #
#-----------------------------------------------------------#
.hidden_appimage_script: &appimage_script_steps
image: registry.gitlab.com/openrgbdevelopers/openrgb-linux-ci-deb-builder:trixie-${TGT_ARCH}
image: registry.gitlab.com/openrgbdevelopers/openrgb-linux-ci-deb-builder:bookworm-${TGT_ARCH}
tags:
- linux
- ${TGT_ARCH}
@@ -87,6 +87,8 @@ before_script:
name: "${CI_PROJECT_NAME}_Linux_${TGT_NAME}_${CI_COMMIT_SHORT_SHA}"
paths:
- OpenRGB-${TGT_PATH}.AppImage
- 60-openrgb.rules
- README.md
expire_in: 30 days
rules:
@@ -288,13 +290,13 @@ before_script:
<<: *debian_script_steps
#-----------------------------------------------------------#
# Linux (.rpm) Fedora 44 amd64 Build Target #
# Linux (.rpm, F43) 64-bit Build Target #
#-----------------------------------------------------------#
"Linux amd64 .rpm (Fedora 44)":
image: fedora:44
"Linux 64 F43 rpm":
image: fedora:43
stage: build
script:
- dnf install rpmdevtools dnf-plugins-core libcurl-devel qt6-qtbase-devel git wget -y
- dnf install rpmdevtools dnf-plugins-core libcurl-devel qt5-qtbase-devel git wget -y
- 'wget -O hidapi-hotplug.zip https://gitlab.com/OpenRGBDevelopers/hidapi-hotplug/-/jobs/artifacts/master/download?job=Linux+64+F43+rpm'
- unzip hidapi-hotplug.zip
- dnf install ./*.rpm -y
@@ -310,7 +312,7 @@ before_script:
- cd ${CI_PROJECT_DIR}
artifacts:
name: "${CI_PROJECT_NAME}_Linux_amd64_rpm_${CI_COMMIT_SHORT_SHA}"
name: "${CI_PROJECT_NAME}_Linux_64_rpm_${CI_COMMIT_SHORT_SHA}"
paths:
- openrgb*.rpm
expire_in: 30 days
@@ -318,44 +320,6 @@ before_script:
rules:
- !reference [.upstream_rules, rules]
#-----------------------------------------------------------#
# OpenRGB Common Flatpak Build Steps #
#-----------------------------------------------------------#
.hidden_flatpak_script: &flatpak_script_steps
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.10
stage: build
tags:
- linux
- ${TGT_ARCH}
script:
- flatpak-builder --user --disable-rofiles-fuse --repo=build/repo build/flatpak-build flatpak/org.openrgb.OpenRGB.yaml
- flatpak build-bundle build/repo org.openrgb.OpenRGB.flatpak org.openrgb.OpenRGB
artifacts:
name: "${CI_PROJECT_NAME}_Linux_${TGT_ARCH}_Flatpak_${CI_COMMIT_SHORT_SHA}"
paths:
- org.openrgb.OpenRGB.flatpak
expire_in: 30 days
rules:
- !reference [.upstream_rules, rules]
#-----------------------------------------------------------#
# Linux Flatpak amd64 Build Target #
#-----------------------------------------------------------#
"Linux amd64 Flatpak":
variables:
TGT_ARCH: "amd64"
<<: *flatpak_script_steps
#-----------------------------------------------------------#
# Linux Flatpak arm64 Build Target #
#-----------------------------------------------------------#
"Linux arm64 Flatpak":
variables:
TGT_ARCH: "arm64"
<<: *flatpak_script_steps
#-----------------------------------------------------------#
# Debian i386 Bookworm test #
#-----------------------------------------------------------#
@@ -423,10 +387,10 @@ before_script:
- !reference [.upstream_rules, rules]
#-----------------------------------------------------------#
# Fedora amd64 44 test #
# Fedora 64 v43 test #
#-----------------------------------------------------------#
"Fedora 64 44":
image: fedora:44
"Fedora 64 v43":
image: fedora:43
stage: test
script:
- dnf install unzip wget -y
@@ -437,9 +401,9 @@ before_script:
- openrgb --version
- dnf5 -y remove openrgb
dependencies:
- "Linux amd64 .rpm (Fedora 44)"
- "Linux 64 F43 rpm"
needs:
- "Linux amd64 .rpm (Fedora 44)"
- "Linux 64 F43 rpm"
rules:
- !reference [.upstream_rules, rules]
-158
View File
@@ -1,158 +0,0 @@
/*---------------------------------------------------------*\
| AutoStart-Flatpak.cpp |
| |
| Autostart implementation for Linux via Flatpak Portal |
| |
| Adam Honse <[email protected]> 04 Sep 2026 |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "AutoStart-Flatpak.h"
#include <gio/gio.h>
AutoStart::AutoStart(std::string name)
{
InitAutoStart(name);
}
void AutoStart::InitAutoStart(std::string name)
{
app_name = name;
}
bool AutoStart::CallBackgroundPortal(bool enable, const AutoStartInfo& autostart_info)
{
GError* error = nullptr;
GDBusConnection* connection = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, &error);
if(!connection)
{
if(error)
{
g_error_free(error);
}
return false;
}
/*-----------------------------------------------------*\
| FreeDesktop Background Portal parameters |
\*-----------------------------------------------------*/
const char* bus_name = "org.freedesktop.portal.Desktop";
const char* object_path = "/org/freedesktop/portal/desktop";
const char* interface_name = "org.freedesktop.portal.Background";
const char* method_name = "RequestBackground";
/*-----------------------------------------------------*\
| Build the options dictionary (vardict) required by |
| the Portal |
\*-----------------------------------------------------*/
GVariantBuilder options_builder;
g_variant_builder_init(&options_builder, G_VARIANT_TYPE_VARDICT);
/*-----------------------------------------------------*\
| Reason shown to the user in the system settings / |
| permission prompt |
\*-----------------------------------------------------*/
std::string reason_str = "Allow " + app_name + " to launch automatically at startup.";
g_variant_builder_add(&options_builder, "{sv}", "reason", g_variant_new_string(reason_str.c_str()));
/*-----------------------------------------------------*\
| Toggle the autostart feature state |
\*-----------------------------------------------------*/
g_variant_builder_add(&options_builder, "{sv}", "autostart", g_variant_new_boolean(enable));
if(enable)
{
/*-------------------------------------------------*\
| Pass command-line arguments to the background |
| request |
\*-------------------------------------------------*/
GVariantBuilder arg_builder;
g_variant_builder_init(&arg_builder, G_VARIANT_TYPE("as"));
/*-------------------------------------------------*\
| Add the primary executable path inside the |
| Flatpak sandbox |
\*-------------------------------------------------*/
g_variant_builder_add(&arg_builder, "s", GetExePath().c_str());
/*-------------------------------------------------*\
| Append any additional arguments provided by |
| OpenRGB's autostart config |
\*-------------------------------------------------*/
if(!autostart_info.args.empty())
{
g_variant_builder_add(&arg_builder, "s", autostart_info.args.c_str());
}
GVariant* commandline_args = g_variant_builder_end(&arg_builder);
g_variant_builder_add(&options_builder, "{sv}", "commandline", commandline_args);
}
/*-----------------------------------------------------*\
| An empty string window handle is used since this is a |
| background configuration request |
\*-----------------------------------------------------*/
const char* parent_window = "";
GVariant* options = g_variant_builder_end(&options_builder);
/*-----------------------------------------------------*\
| Bundle parameters: (s a{sv}) -> Parent window handle, |
| Options dictionary |
\*-----------------------------------------------------*/
GVariant* parameters = g_variant_new("(s@a{sv})", parent_window, options);
GVariant* reply;
reply = g_dbus_connection_call_sync(connection, bus_name, object_path, interface_name, method_name, parameters, G_VARIANT_TYPE("(o)"), G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
g_object_unref(connection);
if(error)
{
g_error_free(error);
return false;
}
if(reply)
{
g_variant_unref(reply);
return true;
}
return false;
}
bool AutoStart::EnableAutoStart(AutoStartInfo autostart_info)
{
return CallBackgroundPortal(true, autostart_info);
}
bool AutoStart::DisableAutoStart()
{
return CallBackgroundPortal(false);
}
bool AutoStart::IsAutoStartEnabled()
{
/*-----------------------------------------------------*\
| The FreeDesktop Background Portal does not provide a |
| stateless status getter. Sandbox permissions are |
| securely managed and persisted internally by the |
| host's portal service. To implement a true state |
| track here, OpenRGB will need to manage a local |
| boolean flag inside its own configuration files (e.g. |
| OpenRGB.json). |
\*-----------------------------------------------------*/
return false;
}
std::string AutoStart::GetExePath()
{
/*-----------------------------------------------------*\
| Flatpak applications always run from this specific |
| exported path hierarchy |
\*-----------------------------------------------------*/
return "/app/bin/openrgb";
}
-31
View File
@@ -1,31 +0,0 @@
/*---------------------------------------------------------*\
| AutoStart-Flatpak.h |
| |
| Autostart implementation for Linux via Flatpak Portal |
| |
| Adam Honse <[email protected]> 04 Sep 2026 |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include "AutoStart.h"
class AutoStart : public AutoStartInterface
{
public:
AutoStart(std::string name);
bool DisableAutoStart();
bool EnableAutoStart(AutoStartInfo autostart_info);
bool IsAutoStartEnabled();
std::string GetExePath();
private:
void InitAutoStart(std::string name);
bool CallBackgroundPortal(bool enable, const AutoStartInfo& autostart_info = {});
std::string app_name;
};
+2 -81
View File
@@ -119,21 +119,6 @@ bool AutoStart::IsAutoStartEnabled()
std::string AutoStart::GetExePath()
{
/*-----------------------------------------------------*\
| When running from an AppImage, the AppImage runtime |
| sets the APPIMAGE environment variable to the path of |
| the AppImage file itself. /proc/self/exe would |
| instead point at the extracted binary inside the |
| temporary squashfs mount, which disappears once the |
| AppImage is closed, so prefer APPIMAGE when it is set |
\*-----------------------------------------------------*/
const char* appimage_path = getenv("APPIMAGE");
if(appimage_path != NULL)
{
return(std::string(appimage_path));
}
/*-----------------------------------------------------*\
| Create the OpenRGB executable path |
\*-----------------------------------------------------*/
@@ -149,64 +134,6 @@ std::string AutoStart::GetExePath()
| Private Methods |
\*---------------------------------------------------------*/
/*---------------------------------------------------------*\
| Escape a single argument for use in a .desktop Exec= |
| field, per the Desktop Entry Specification. |
| |
| Reserved characters must be escaped by wrapping the whole |
| argument in double quotes and backslash-escaping the |
| quote/backquote/backslash/dollar characters, and any |
| literal '%' must be doubled to '%%'. |
\*---------------------------------------------------------*/
static std::string DesktopExecEscape(const std::string& arg)
{
static const std::string reserved = " \t\n\"'`\\><~|&;$*?#()%";
bool needs_quotes = false;
for(char c : arg)
{
if(reserved.find(c) != std::string::npos)
{
needs_quotes = true;
break;
}
}
std::string escaped;
if(needs_quotes)
{
escaped += '"';
}
for(char c : arg)
{
switch(c)
{
case '"':
case '`':
case '\\':
case '$':
escaped += '\\';
escaped += c;
break;
case '%':
escaped += "%%";
break;
default:
escaped += c;
break;
}
}
if(needs_quotes)
{
escaped += '"';
}
return(escaped);
}
std::string AutoStart::GenerateDesktopFile(AutoStartInfo autostart_info)
{
/*-----------------------------------------------------*\
@@ -227,17 +154,11 @@ std::string AutoStart::GenerateDesktopFile(AutoStartInfo autostart_info)
/*-----------------------------------------------------*\
| Add the executable path and arguments |
\*-----------------------------------------------------*/
fileContents << "Exec=" << DesktopExecEscape(autostart_info.path);
fileContents << "Exec=" << autostart_info.path;
if (autostart_info.args != "")
{
std::istringstream arg_parser(autostart_info.args);
std::string arg;
while(arg_parser >> arg)
{
fileContents << " " << DesktopExecEscape(arg);
}
fileContents << " " << autostart_info.args;
}
fileContents << std::endl;
+2 -41
View File
@@ -137,43 +137,6 @@ std::string AutoStart::GetExePath()
| Private Methods |
\*---------------------------------------------------------*/
/*---------------------------------------------------------*\
| Escape a string for embedding inside an XML element |
| (used by the .plist generator) to avoid producing |
| malformed property list files. |
\*---------------------------------------------------------*/
static std::string XmlEscape(const std::string& input)
{
std::string escaped;
for(char c : input)
{
switch(c)
{
case '&':
escaped += "&amp;";
break;
case '<':
escaped += "&lt;";
break;
case '>':
escaped += "&gt;";
break;
case '"':
escaped += "&quot;";
break;
case '\'':
escaped += "&apos;";
break;
default:
escaped += c;
break;
}
}
return(escaped);
}
std::string AutoStart::GenerateLaunchAgentFile(AutoStartInfo autostart_info)
{
/*-----------------------------------------------------*\
@@ -190,8 +153,7 @@ std::string AutoStart::GenerateLaunchAgentFile(AutoStartInfo autostart_info)
fileContents << " <string>org.openrgb</string>" << std::endl;
fileContents << " <key>ProgramArguments</key>" << std::endl;
fileContents << " <array>" << std::endl;
fileContents << " <string>" << XmlEscape(autostart_info.path) << "</string>"
<< std::endl;
fileContents << " <string>" << autostart_info.path << "</string>" << std::endl;
if(autostart_info.args != "")
{
@@ -200,8 +162,7 @@ std::string AutoStart::GenerateLaunchAgentFile(AutoStartInfo autostart_info)
while(arg_parser >> arg)
{
fileContents << " <string>" << XmlEscape(arg) << "</string>"
<< std::endl;
fileContents << " <string>" << arg << "</string>" << std::endl;
}
}
+32 -34
View File
@@ -12,7 +12,6 @@
#include <shlobj.h>
#include "AutoStart-Windows.h"
#include "LogManager.h"
#include "StringUtils.h"
#include "filesystem.h"
#include "windows.h"
@@ -72,11 +71,11 @@ bool AutoStart::EnableAutoStart(AutoStartInfo autostart_info)
HRESULT result;
IShellLinkW* shellLink = NULL;
std::wstring exepathw = StringUtils::string_to_wstring(autostart_info.path);
std::wstring argumentsw = StringUtils::string_to_wstring(autostart_info.args);
std::wstring startupfilepathw = StringUtils::string_to_wstring(autostart_file);
std::wstring descriptionw = StringUtils::string_to_wstring(autostart_info.desc);
std::wstring iconw = StringUtils::string_to_wstring(autostart_info.path);
std::wstring exepathw = utf8_decode(autostart_info.path);
std::wstring argumentsw = utf8_decode(autostart_info.args);
std::wstring startupfilepathw = utf8_decode(autostart_file);
std::wstring descriptionw = utf8_decode(autostart_info.desc);
std::wstring iconw = utf8_decode(autostart_info.path);
result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&shellLink);
@@ -151,16 +150,11 @@ std::string AutoStart::GetExePath()
/*-----------------------------------------------------*\
| Create the OpenRGB executable path |
\*-----------------------------------------------------*/
wchar_t exepath[MAX_PATH] = L"";
char exepath[MAX_PATH] = "";
DWORD count = GetModuleFileNameW(NULL, exepath, MAX_PATH);
DWORD count = GetModuleFileNameA(NULL, exepath, MAX_PATH);
if(count == 0)
{
return(std::string());
}
return(StringUtils::wstring_to_string(std::wstring(exepath, count)));
return(std::string(exepath, (count > 0) ? count : 0));
}
/*---------------------------------------------------------*\
@@ -170,39 +164,43 @@ std::string AutoStart::GetExePath()
void AutoStart::InitAutoStart(std::string name)
{
wchar_t startMenuPath[MAX_PATH];
char startMenuPath[MAX_PATH];
autostart_name = name;
/*-----------------------------------------------------*\
| Get startup applications path |
\*-----------------------------------------------------*/
HRESULT result = SHGetFolderPathW(NULL, CSIDL_PROGRAMS, NULL, 0, startMenuPath);
HRESULT result = SHGetFolderPathA(NULL, CSIDL_PROGRAMS, NULL, 0, startMenuPath);
if(SUCCEEDED(result))
{
std::string autostart_dir = StringUtils::wstring_to_string(startMenuPath);
autostart_dir += "\\Startup\\";
autostart_file = std::string(startMenuPath);
/*-------------------------------------------------*\
| Create the directory if it doesn't exist |
\*-------------------------------------------------*/
std::error_code ec;
bool success = true;
if(!filesystem::exists(autostart_dir))
{
success = filesystem::create_directories(autostart_dir, ec);
}
if(success)
{
autostart_file = autostart_dir + autostart_name + ".lnk";
}
autostart_file += "\\Startup\\" + autostart_name + ".lnk";
}
else
{
autostart_file.clear();
}
}
/*---------------------------------------------------------*\
| Convert an UTF8 string to a wide Unicode String |
| (from wmi.cpp) |
\*---------------------------------------------------------*/
std::wstring AutoStart::utf8_decode(const std::string& str)
{
if(str.empty())
{
return std::wstring();
}
int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int) str.size(), nullptr, 0);
std::wstring wstrTo(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int) str.size(), &wstrTo[0], size_needed);
return(wstrTo);
}
+1
View File
@@ -24,4 +24,5 @@ public:
private:
void InitAutoStart(std::string name);
std::wstring utf8_decode(const std::string& str);
};
-6
View File
@@ -40,15 +40,9 @@ protected:
#include "AutoStart-Windows.h"
#endif
#ifdef FLATPAK_BUILD
#include "AutoStart-Flatpak.h"
#endif
#ifdef __linux__
#ifndef FLATPAK_BUILD
#include "AutoStart-Linux.h"
#endif
#endif
#ifdef __APPLE__
#include "AutoStart-MacOS.h"
@@ -1,199 +0,0 @@
/*---------------------------------------------------------*\
| ASRockGPUSMBusController.cpp |
| |
| Driver for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "ASRockGPUSMBusController.h"
#include "LogManager.h"
#include <chrono>
#include <cstdio>
#include <cstring>
#include <thread>
using namespace std::chrono_literals;
ASRockGPUSMBusController::ASRockGPUSMBusController(i2c_smbus_interface* bus, asrock_gpu_dev_id dev, std::string name)
{
this->bus = bus;
this->dev = dev;
this->device_name = name;
this->num_channels = 0;
memset(enabled_channels, 0, sizeof(enabled_channels));
memset(led_counts, 0, sizeof(led_counts));
}
ASRockGPUSMBusController::~ASRockGPUSMBusController()
{
}
std::string ASRockGPUSMBusController::GetDeviceLocation()
{
std::string return_string(bus->info.device_name);
char addr[5];
snprintf(addr, 5, "0x%02X", dev);
return_string.append(", address ");
return_string.append(addr);
return("I2C: " + return_string);
}
std::string ASRockGPUSMBusController::GetDeviceName()
{
return(device_name);
}
bool ASRockGPUSMBusController::WriteBlock(uint8_t command, uint8_t subcommand, const uint8_t* data, uint8_t len)
{
/*-----------------------------------------------------*\
| SignalRGB packet format |
| [command, 0, subcommand, data...] |
| Uses pure I2C rather than SMBus |
\*-----------------------------------------------------*/
uint8_t packet[12];
memset(packet, 0, sizeof(packet));
packet[0] = command;
packet[1] = 0x00;
packet[2] = subcommand;
if(data != nullptr && len > 0)
{
uint8_t copy_len = (len > 9) ? 9 : len;
memcpy(&packet[3], data, copy_len);
}
int size = 12;
int result = bus->i2c_write_block(dev, size, packet);
if(result < 0)
{
LOG_DEBUG("[ASRock GPU] Write block failed: %d", result);
return(false);
}
return(true);
}
bool ASRockGPUSMBusController::ReadBlock(uint8_t* response, uint8_t len)
{
memset(response, 0, len);
int size = len;
int result = bus->i2c_read_block(dev, &size, response);
if(result < 0)
{
LOG_DEBUG("[ASRock GPU] Read block failed: %d", result);
return(false);
}
return(true);
}
bool ASRockGPUSMBusController::DetectChannels()
{
/*-----------------------------------------------------*\
| Get enabled channels |
| Command 0x14, subcommand 0x01 |
\*-----------------------------------------------------*/
if(!WriteBlock(0x14, 0x01, nullptr, 0))
{
LOG_WARNING("[ASRock GPU] Failed to request enabled channels");
return(false);
}
std::this_thread::sleep_for(20ms);
uint8_t response[I2C_SMBUS_BLOCK_MAX];
if(!ReadBlock(response, sizeof(response)))
{
LOG_WARNING("[ASRock GPU] Failed to read enabled channels");
return(false);
}
LOG_TRACE("[ASRock GPU] Enabled channels response: %02X %02X %02X %02X %02X %02X", response[0], response[1], response[2], response[3], response[4], response[5]);
/*-----------------------------------------------------*\
| Response format |
| Bytes 4-5 contain the channel bitmap |
\*-----------------------------------------------------*/
uint16_t channel_bits = response[4] | (response[5] << 8);
LOG_TRACE("[ASRock GPU] Channel bits: 0x%04X", channel_bits);
/*-----------------------------------------------------*\
| Get LED counts |
| Command 0x14, subcommand 0x02 |
\*-----------------------------------------------------*/
if(!WriteBlock(0x14, 0x02, nullptr, 0))
{
LOG_WARNING("[ASRock GPU] Failed to request LED counts");
return(false);
}
std::this_thread::sleep_for(20ms);
if(!ReadBlock(response, sizeof(response)))
{
LOG_WARNING("[ASRock GPU] Failed to read LED counts");
return(false);
}
num_channels = 0;
for(int i = 0; i < 16; i++)
{
if(channel_bits & (1 << i))
{
enabled_channels[num_channels] = i;
led_counts[num_channels] = response[4 + i];
const char* name = "Unknown";
if(i == ASROCK_GPU_CHANNEL_ARGB_HEADER)
{
name = "ARGB Header";
}
else if(i == ASROCK_GPU_CHANNEL_TOP_SIDE)
{
name = "Top Side";
}
else if(i == ASROCK_GPU_CHANNEL_FAN)
{
name = "Fan";
}
LOG_DEBUG("[ASRock GPU] Channel %d (%s) active with %d LEDs", i, name, response[4 + i]);
num_channels++;
}
}
LOG_DEBUG("[ASRock GPU] Found %d active channels", num_channels);
return(num_channels > 0);
}
void ASRockGPUSMBusController::SetChannelColor(uint8_t channel_idx, uint8_t red, uint8_t green, uint8_t blue)
{
/*-----------------------------------------------------*\
| SignalRGB color packet |
| Command 0x10, subcommand is the channel index |
\*-----------------------------------------------------*/
uint8_t data[8] =
{
0x01, // Mode: direct/static
red,
green,
blue,
0x80, // Effect speed
0xFF, // Brightness
0x00, // Effect direction
0x1A // Unknown constant
};
if(!WriteBlock(0x10, channel_idx, data, sizeof(data)))
{
LOG_WARNING("[ASRock GPU] Failed to set color for channel %d", channel_idx);
}
}
@@ -1,55 +0,0 @@
/*---------------------------------------------------------*\
| ASRockGPUSMBusController.h |
| |
| Driver for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include "i2c_smbus.h"
typedef uint8_t asrock_gpu_dev_id;
#define ASROCK_GPU_CONTROLLER_NAME "ASRock GPU SMBus Controller"
#define ASROCK_GPU_SMBUS_ADDRESS 0x36
/*-----------------------------------------------------*\
| ASRock GPU Channel Names (from SignalRGB) |
\*-----------------------------------------------------*/
enum
{
ASROCK_GPU_CHANNEL_ARGB_HEADER = 3,
ASROCK_GPU_CHANNEL_TOP_SIDE = 6,
ASROCK_GPU_CHANNEL_FAN = 7,
};
class ASRockGPUSMBusController
{
public:
ASRockGPUSMBusController(i2c_smbus_interface* bus, asrock_gpu_dev_id dev, std::string name);
~ASRockGPUSMBusController();
std::string GetDeviceLocation();
std::string GetDeviceName();
void SetChannelColor(uint8_t channel_idx, uint8_t red, uint8_t green, uint8_t blue);
bool DetectChannels();
uint8_t enabled_channels[16];
uint8_t led_counts[16];
uint8_t num_channels;
private:
i2c_smbus_interface* bus;
asrock_gpu_dev_id dev;
std::string device_name;
bool WriteBlock(uint8_t command, uint8_t subcommand, const uint8_t* data, uint8_t len);
bool ReadBlock(uint8_t* response, uint8_t len);
};
@@ -1,47 +0,0 @@
/*---------------------------------------------------------*\
| ASRockGPUSMBusControllerDetect.cpp |
| |
| Detector for ASRock GPU RGB controllers |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "DetectionManager.h"
#include "ASRockGPUSMBusController.h"
#include "LogManager.h"
#include "RGBController_ASRockGPUSMBus.h"
#include "i2c_amd_gpu.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;
if(bus->info.pci_vendor == AMD_GPU_VEN && !is_amd_gpu_i2c_bus(bus))
{
return(detected_controllers);
}
LOG_DEBUG("[ASRock GPU] Checking GPU I2C bus for RGB controller at 0x%02X", 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);
REGISTER_I2C_PCI_DETECTOR("ASRock Radeon RX 9070 XT Steel Legend Dark", DetectASRockGPUSMBusControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, ASROCK_SUB_VEN, 0x5414, ASROCK_GPU_SMBUS_ADDRESS);
REGISTER_I2C_PCI_DETECTOR("ASRock Radeon RX 9070 XT Taichi 16GB OC", DetectASRockGPUSMBusControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, ASROCK_SUB_VEN, 0x5401, ASROCK_GPU_SMBUS_ADDRESS);
REGISTER_I2C_PCI_DETECTOR("ASRock Radeon RX 9060 XT Steel Legend", DetectASRockGPUSMBusControllers, AMD_GPU_VEN, AMD_NAVI44_DEV, ASROCK_SUB_VEN, 0x5407, ASROCK_GPU_SMBUS_ADDRESS);
@@ -1,140 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_ASRockGPUSMBus.cpp |
| |
| RGBController for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_ASRockGPUSMBus.h"
/**------------------------------------------------------------------*\
@name ASRock GPU SMBus
@category GPU
@type I2C
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectASRockGPUSMBusControllers
@comment
\*-------------------------------------------------------------------*/
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()
{
Shutdown();
delete controller;
}
void RGBController_ASRockGPUSMBus::SetupZones()
{
/*-----------------------------------------------------*\
| Set up zones - one zone per channel |
\*-----------------------------------------------------*/
for(unsigned int i = 0; i < controller->num_channels; i++)
{
uint8_t channel_idx = controller->enabled_channels[i];
zone new_zone;
new_zone.type = ZONE_TYPE_SINGLE;
new_zone.leds_min = 1;
new_zone.leds_max = 1;
new_zone.leds_count = 1;
/*-----------------------------------------------------*\
| Channel names from SignalRGB |
\*-----------------------------------------------------*/
if(channel_idx == ASROCK_GPU_CHANNEL_ARGB_HEADER)
{
new_zone.name = "ARGB Header";
}
else if(channel_idx == ASROCK_GPU_CHANNEL_TOP_SIDE)
{
new_zone.name = "Top Side";
}
else if(channel_idx == ASROCK_GPU_CHANNEL_FAN)
{
new_zone.name = "Fan";
}
else
{
new_zone.name = "Channel " + std::to_string(channel_idx);
}
zones.push_back(new_zone);
/*-----------------------------------------------------*\
| Set up LED for this zone |
\*-----------------------------------------------------*/
led new_led;
new_led.name = new_zone.name;
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_ASRockGPUSMBus::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*-----------------------------------------------------*\
| This device does not support resizing zones |
\*-----------------------------------------------------*/
}
void RGBController_ASRockGPUSMBus::DeviceUpdateLEDs()
{
for(unsigned int i = 0; i < controller->num_channels; i++)
{
uint8_t channel_idx = controller->enabled_channels[i];
RGBColor color = colors[i];
controller->SetChannelColor(channel_idx,
RGBGetRValue(color),
RGBGetGValue(color),
RGBGetBValue(color));
}
}
void RGBController_ASRockGPUSMBus::UpdateZoneLEDs(int zone)
{
uint8_t channel_idx = controller->enabled_channels[zone];
RGBColor color = colors[zone];
controller->SetChannelColor(channel_idx,
RGBGetRValue(color),
RGBGetGValue(color),
RGBGetBValue(color));
}
void RGBController_ASRockGPUSMBus::UpdateSingleLED(int led)
{
UpdateZoneLEDs(led);
}
void RGBController_ASRockGPUSMBus::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
@@ -1,34 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_ASRockGPUSMBus.h |
| |
| RGBController for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "ASRockGPUSMBusController.h"
class RGBController_ASRockGPUSMBus : public RGBController
{
public:
RGBController_ASRockGPUSMBus(ASRockGPUSMBusController* controller_ptr);
~RGBController_ASRockGPUSMBus();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
ASRockGPUSMBusController* controller;
};
@@ -1,50 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidKeyboard.cpp |
| |
| Detector for Acer Nitro HID Keyboard |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <vector>
#include "DetectionManager.h"
#include "AcerNitroHidKeyboardController.h"
#include "RGBController_AcerNitroHidKeyboard.h"
#include "RGBController_AcerNitroHidLed.h"
/*---------------------------------------------------------*\
| HID vendor and product IDs for ENEK EC in Acer Nitro |
\*---------------------------------------------------------*/
#define ACER_ENEC_VID 0x0CF2
#define ACER_ENEC_PID 0x5130
#define ACER_ENEC_USAGE_PAGE 0xFF5A
#define ACER_ENEC_USAGE 0x01
DetectedControllers DetectAcerHIDControllers(hid_device_info* info, const std::string&)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
/*-------------------------------------------------*\
| Controller is shared between keyboard and onboard |
| led |
\*-------------------------------------------------*/
std::shared_ptr<AcerNitroHidKeyboardController> controller = std::make_shared<AcerNitroHidKeyboardController>(dev, info->path);
RGBController_AcerNitroHidKeyboard* rgb_kb_controller = new RGBController_AcerNitroHidKeyboard(controller);
RGBController_AcerNitroHidLed* rgb_led_controller = new RGBController_AcerNitroHidLed(controller);
detected_controllers.push_back(rgb_kb_controller);
detected_controllers.push_back(rgb_led_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_PU("AcerNitroHidKeyboardController", DetectAcerHIDControllers, ACER_ENEC_VID, ACER_ENEC_PID, ACER_ENEC_USAGE_PAGE, ACER_ENEC_USAGE);
@@ -1,80 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidKeyboard.cpp |
| |
| Driver for Acer Nitro Keyboard |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "AcerNitroHidKeyboardController.h"
AcerNitroHidKeyboardController::AcerNitroHidKeyboardController(hid_device *dev_handle, const char *path)
{
this->location = path;
this->dev = dev_handle;
device_name = "Acer Nitro HID Keyboard";
}
AcerNitroHidKeyboardController::~AcerNitroHidKeyboardController()
{
if(dev)
{
hid_close(dev);
}
}
std::string AcerNitroHidKeyboardController::GetLocation() const
{
return("HID: " + location);
}
std::string AcerNitroHidKeyboardController::GetDeviceName() const
{
return(device_name);
}
void AcerNitroHidKeyboardController::SetLEDs(const std::vector<RGBColor>& colors, unsigned char brightness, unsigned char device_id)
{
std::lock_guard<std::mutex> guard(device_mutex);
for(unsigned int led_idx = 0; (led_idx < colors.size()) && (led_idx < ACER_HID_NUM_ZONES); led_idx++)
{
/*-------------------------------------------------*\
| Convert colors to RGB format |
\*-------------------------------------------------*/
unsigned char r = RGBGetRValue(colors[led_idx]);
unsigned char g = RGBGetGValue(colors[led_idx]);
unsigned char b = RGBGetBValue(colors[led_idx]);
/*-------------------------------------------------*\
| Send packet |
\*-------------------------------------------------*/
unsigned char zone_select = (unsigned char)(1<<led_idx);
unsigned char packet[ACER_HID_PACKET_SIZE] = {0xA4, device_id, ACER_HID_MODE_STATIC, brightness, 0, 1, r, g, b, zone_select, 0};
hid_send_feature_report(dev, packet, ACER_HID_PACKET_SIZE);
}
}
void AcerNitroHidKeyboardController::SetAllLEDs(const RGBColor &color, unsigned char brightness, unsigned char speed, unsigned char direction, unsigned char mode, unsigned char device_id)
{
std::lock_guard<std::mutex> guard(device_mutex);
/*-----------------------------------------------------*\
| Convert color to RGB format |
\*-----------------------------------------------------*/
unsigned char r = RGBGetRValue(color);
unsigned char g = RGBGetGValue(color);
unsigned char b = RGBGetBValue(color);
/*-----------------------------------------------------*\
| Send packet |
| Zone select = All zones |
\*-----------------------------------------------------*/
unsigned char zone_select = 0x0F;
unsigned char packet[ACER_HID_PACKET_SIZE] = {0xA4, device_id, mode, brightness, speed, direction, r, g, b, zone_select, 0};
hid_send_feature_report(dev, packet, ACER_HID_PACKET_SIZE);
}
@@ -1,56 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidKeyboard.cpp |
| |
| Driver for Acer Nitro Keyboard |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
enum
{
ACER_HID_MODE_DIRECT = 1,
ACER_HID_MODE_STATIC = 2,
ACER_HID_MODE_BREATHING = 4,
ACER_HID_MODE_NEON = 5,
ACER_HID_MODE_OPMODE = 6,
ACER_HID_MODE_WAVE = 7,
ACER_HID_MODE_SHIFTING = 8,
ACER_HID_MODE_ZOOM = 9,
ACER_HID_MODE_METEOR = 10,
ACER_HID_MODE_TWINKLING = 11,
};
#define ACER_HID_NAME_BUFFER_SIZE 255
#define ACER_HID_NUM_ZONES 4
#define ACER_HID_PACKET_SIZE 11
#include <mutex>
#include <string>
#include <vector>
#include <hidapi.h>
#include "RGBControllerInterface.h"
class AcerNitroHidKeyboardController
{
public:
AcerNitroHidKeyboardController(hid_device *dev_handle, const char *path);
~AcerNitroHidKeyboardController();
std::string GetDeviceName() const;
std::string GetSerial() const;
std::string GetLocation() const;
void SetLEDs(const std::vector<RGBColor> &colors, unsigned char brightness, unsigned char device_id);
void SetAllLEDs(const RGBColor &color, unsigned char brightness, unsigned char speed, unsigned char direction, unsigned char mode, unsigned char device_id);
private:
hid_device *dev;
std::string device_name;
std::string location;
std::mutex device_mutex;
};
@@ -1,231 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidKeyboard.cpp |
| |
| RGBController for Acer Nitro HID Keyboard |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_AcerNitroHidKeyboard.h"
/**------------------------------------------------------------------*\
@name Acer Nitro HID Keyboard
@category Laptop
@type HID
@save :x:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectAcerHIDControllers
@comment
\*-------------------------------------------------------------------*/
RGBController_AcerNitroHidKeyboard::RGBController_AcerNitroHidKeyboard(std::shared_ptr<AcerNitroHidKeyboardController> controller)
{
this->controller = controller;
device_id = 0x21;
name = "Acer Nitro HID Keyboard";
vendor = "Acer";
type = DEVICE_TYPE_LAPTOP;
description = "Acer Nitro HID Keyboard Device";
location = controller->GetLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = ACER_HID_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS ;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.brightness_min = 0;
Direct.brightness_max = 100;
Direct.brightness = 100;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = ACER_HID_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.brightness_min = 0;
Static.brightness_max = 100;
Static.brightness = 100;
Static.colors_min = 1;
Static.colors_max = 1;
Static.colors.resize(1);
Static.speed = 0;
modes.push_back(Static);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = ACER_HID_MODE_BREATHING;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
Breathing.brightness_min = 0;
Breathing.brightness_max = 100;
Breathing.brightness = 100;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.speed_min = 1;
Breathing.speed_max = 9;
Breathing.speed = 4;
Breathing.colors.resize(1);
modes.push_back(Breathing);
mode Neon;
Neon.name = "Neon";
Neon.value = ACER_HID_MODE_NEON;
Neon.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Neon.color_mode = MODE_COLORS_NONE;
Neon.brightness_min = 0;
Neon.brightness_max = 100;
Neon.brightness = 100;
Neon.speed_min = 1;
Neon.speed_max = 9;
Neon.speed = 4;
modes.push_back(Neon);
mode Wave;
Wave.name = "Wave";
Wave.value = ACER_HID_MODE_WAVE;
Wave.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR;
Wave.color_mode = MODE_COLORS_NONE;
Wave.brightness_min = 0;
Wave.brightness_max = 100;
Wave.brightness = 100;
Wave.speed_min = 1;
Wave.speed_max = 9;
Wave.speed = 4;
Wave.direction = 0;
modes.push_back(Wave);
mode Shifting;
Shifting.name = "Shifting";
Shifting.value = ACER_HID_MODE_SHIFTING;
Shifting.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Shifting.color_mode = MODE_COLORS_NONE;
Shifting.brightness_min = 0;
Shifting.brightness_max = 100;
Shifting.brightness = 100;
Shifting.speed_min = 1;
Shifting.speed_max = 9;
Shifting.speed = 4;
modes.push_back(Shifting);
mode Zoom;
Zoom.name = "Zoom";
Zoom.value = ACER_HID_MODE_ZOOM;
Zoom.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Zoom.color_mode = MODE_COLORS_NONE;
Zoom.brightness_min = 0;
Zoom.brightness_max = 100;
Zoom.brightness = 100;
Zoom.speed_min = 1;
Zoom.speed_max = 9;
Zoom.speed = 4;
modes.push_back(Zoom);
mode Meteor;
Meteor.name = "Meteor";
Meteor.value = ACER_HID_MODE_METEOR;
Meteor.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Meteor.color_mode = MODE_COLORS_NONE;
Meteor.brightness_min = 0;
Meteor.brightness_max = 100;
Meteor.brightness = 100;
Meteor.speed_min = 1;
Meteor.speed_max = 9;
Meteor.speed = 4;
modes.push_back(Meteor);
mode Twinkling;
Twinkling.name = "Twinkling";
Twinkling.value = ACER_HID_MODE_TWINKLING;
Twinkling.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Twinkling.color_mode = MODE_COLORS_NONE;
Twinkling.brightness_min = 0;
Twinkling.brightness_max = 100;
Twinkling.brightness = 100;
Twinkling.colors_min = 1;
Twinkling.colors_max = 1;
Twinkling.speed_min = 1;
Twinkling.speed_max = 9;
Twinkling.speed = 4;
modes.push_back(Twinkling);
SetupZones();
}
RGBController_AcerNitroHidKeyboard::~RGBController_AcerNitroHidKeyboard()
{
Shutdown();
}
void RGBController_AcerNitroHidKeyboard::SetupZones()
{
zone* new_zone = new zone;
new_zone->name = "Keyboard Backlight Zone";
new_zone->type = ZONE_TYPE_LINEAR;
new_zone->leds_min = 4;
new_zone->leds_max = 4;
new_zone->leds_count = 4;
zones.push_back(*new_zone);
for(std::size_t zone_idx = 0; zone_idx < ACER_HID_NUM_ZONES; zone_idx++)
{
led* new_led = new led();
new_led->name = "Keyboard Backlight Zone ";
new_led->name.append(std::to_string(zone_idx + 1));
new_led->value = (unsigned int)leds.size();
leds.push_back(*new_led);
}
SetupColors();
}
void RGBController_AcerNitroHidKeyboard::DeviceUpdateLEDs()
{
mode& current_mode = modes[active_mode];
if(current_mode.value == ACER_HID_MODE_DIRECT)
{
controller->SetLEDs(colors, current_mode.brightness, device_id);
}
else
{
RGBColor color = current_mode.colors.size()==0? (RGBColor)0 : current_mode.colors[0];
unsigned char direction = 1;
if((modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) && modes[active_mode].direction)
{
direction = 2;
}
controller->SetAllLEDs(color, current_mode.brightness, current_mode.speed, direction, current_mode.value, device_id);
}
}
void RGBController_AcerNitroHidKeyboard::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_AcerNitroHidKeyboard::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_AcerNitroHidKeyboard::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
@@ -1,33 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidKeyboard.cpp |
| |
| RGBController for Acer Nitro HID Keyboard |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "AcerNitroHidKeyboardController.h"
class RGBController_AcerNitroHidKeyboard : public RGBController
{
public:
RGBController_AcerNitroHidKeyboard(std::shared_ptr<AcerNitroHidKeyboardController> controller);
~RGBController_AcerNitroHidKeyboard();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
private:
std::shared_ptr<AcerNitroHidKeyboardController> controller;
unsigned int device_id;
};
@@ -1,152 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidLed.cpp |
| |
| RGBController for Acer Nitro HID LED |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_AcerNitroHidLed.h"
/**------------------------------------------------------------------*\
@name Acer Nitro HID Led
@category Laptop
@type HID
@save :x:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectAcerHIDControllers
@comment
\*-------------------------------------------------------------------*/
RGBController_AcerNitroHidLed::RGBController_AcerNitroHidLed(std::shared_ptr<AcerNitroHidKeyboardController> controller)
{
this->controller = controller;
device_id = 0x65;
name = "Acer Nitro HID LED Device";
vendor = "Acer";
type = DEVICE_TYPE_LIGHT;
description = "Acer Nitro HID LED Device";
location = controller->GetLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = ACER_HID_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS ;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.brightness_min = 0;
Direct.brightness_max = 100;
Direct.brightness = 100;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = ACER_HID_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.brightness_min = 0;
Static.brightness_max = 100;
Static.brightness = 100;
Static.colors_min = 1;
Static.colors_max = 1;
Static.colors.resize(1);
Static.speed = 0;
modes.push_back(Static);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = ACER_HID_MODE_BREATHING;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
Breathing.brightness_min = 0;
Breathing.brightness_max = 100;
Breathing.brightness = 100;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.speed_min = 1;
Breathing.speed_max = 5;
Breathing.speed = 4;
Breathing.colors.resize(1);
modes.push_back(Breathing);
mode Neon;
Neon.name = "Neon";
Neon.value = ACER_HID_MODE_NEON;
Neon.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Neon.color_mode = MODE_COLORS_NONE;
Neon.brightness_min = 0;
Neon.brightness_max = 100;
Neon.brightness = 100;
Neon.speed_min = 1;
Neon.speed_max = 5;
Neon.speed = 4;
modes.push_back(Neon);
SetupZones();
}
RGBController_AcerNitroHidLed::~RGBController_AcerNitroHidLed()
{
Shutdown();
}
void RGBController_AcerNitroHidLed::SetupZones()
{
zone* new_zone = new zone;
new_zone->name = "LED";
new_zone->type = ZONE_TYPE_LINEAR;
new_zone->leds_min = 1;
new_zone->leds_max = 1;
new_zone->leds_count = 1;
zones.push_back(*new_zone);
led* new_led = new led();
new_led->name = "LED";
new_led->value = (unsigned int)leds.size();
leds.push_back(*new_led);
SetupColors();
}
void RGBController_AcerNitroHidLed::DeviceUpdateLEDs()
{
mode& current_mode = modes[active_mode];
if(current_mode.value == ACER_HID_MODE_DIRECT)
{
controller->SetLEDs(colors, current_mode.brightness, device_id);
}
else
{
RGBColor color = current_mode.colors.size()==0? (RGBColor)0 : current_mode.colors[0];
unsigned char direction = 1;
if((modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) && modes[active_mode].direction)
{
direction = 2;
}
controller->SetAllLEDs(color, current_mode.brightness, current_mode.speed, direction, current_mode.value, device_id);
}
}
void RGBController_AcerNitroHidLed::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_AcerNitroHidLed::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_AcerNitroHidLed::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
@@ -1,35 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_AcerNitroHidLed.cpp |
| |
| RGBController for Acer Nitro HID LED |
| |
| Marek Pokropiński Jul 27 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "AcerNitroHidKeyboardController.h"
#include "RGBController_AcerNitroHidKeyboard.h"
class RGBController_AcerNitroHidLed : public RGBController
{
public:
RGBController_AcerNitroHidLed(std::shared_ptr<AcerNitroHidKeyboardController> controller);
~RGBController_AcerNitroHidLed();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
private:
std::shared_ptr<AcerNitroHidKeyboardController> controller;
unsigned int device_id;
};
@@ -32,7 +32,6 @@ static const std::map<alienware_platform_id, uint8_t> zone_quirks_table =
{ 0x0C01, 4 }, // Dell G5 SE 5505
{ 0x0A01, 16 }, // Dell G7 15 7500
{ 0x0E03, 4 }, // Dell G15 5511
{ 0x0E07, 4 }, // Dell G15 5520
{ 0x0E0A, 4 } // Dell G15 5530
};
@@ -50,21 +49,9 @@ static const std::map<alienware_platform_id, std::vector<const char*>> zone_name
"Light Bar 7", "Light Bar 8", "Light Bar 9",
"Light Bar 10", "Light Bar 11", "Light Bar 12" } },
{ 0x0E03, { "Left", "Middle", "Right", "Numpad" } },
{ 0x0E07, { "Left", "Middle", "Right", "Numpad" } },
{ 0x0E0A, { "Left", "Middle", "Right", "Numpad" } }
};
/*---------------------------------------------------------*\
| Mapping the platform ID to the actual hardware zone IDs |
\*---------------------------------------------------------*/
static const std::map<alienware_platform_id, std::vector<uint8_t>> zone_ids_table =
{
{ 0x0C01, { 0x00, 0x01, 0x02, 0x03 } }, // Dell G5 SE 5505
{ 0x0E03, { 0x00, 0x01, 0x02, 0x03 } }, // Dell G15 5511
{ 0x0E07, { 0x10, 0x11, 0x12, 0x13 } }, // Dell G15 5520
{ 0x0E0A, { 0x10, 0x11, 0x12, 0x13 } } // Dell G15 5530
};
static void SendHIDReport(hid_device *dev, const unsigned char* usb_buf, size_t usb_buf_size)
{
using namespace std::chrono_literals;
@@ -151,21 +138,6 @@ AlienwareController::AlienwareController(hid_device* dev_handle, const hid_devic
}
}
/*-----------------------------------------------------*\
| Initialize hardware zone IDs |
\*-----------------------------------------------------*/
if(zone_ids_table.count(platform_id))
{
zone_ids = zone_ids_table.at(platform_id);
}
else
{
for(size_t i = 0; i < number_of_zones; i++)
{
zone_ids.push_back(static_cast<uint8_t>(i));
}
}
/*-----------------------------------------------------*\
| Set defaults for all zones |
| It doesn't seem possible to read the controller's |
@@ -183,7 +155,6 @@ AlienwareController::AlienwareController(hid_device* dev_handle, const hid_devic
zones[zone_idx].period = 2000;
zones[zone_idx].tempo = ALIENWARE_TEMPO_MAX;
zones[zone_idx].dim = 0;
zones[zone_idx].direction = 0;
}
/*-----------------------------------------------------*\
@@ -295,53 +266,42 @@ bool AlienwareController::Dim(std::vector<uint8_t> zones, double percent)
return(true);
}
bool result = true;
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
/*-----------------------------------------------------*\
| Split the zone list into packet-sized chunks so it |
| never overflows usb_buf |
| Zero out buffer |
\*-----------------------------------------------------*/
for(size_t offset = 0; offset < zones.size(); offset += ALIENWARE_MAX_ZONES_PER_PACKET)
memset(usb_buf, 0x00, sizeof(usb_buf));
/*-----------------------------------------------------*\
| Set up message packet with leading 00, per hidapi |
\*-----------------------------------------------------*/
uint16_t num_zones = (uint16_t)zones.size();
usb_buf[0x00] = 0x00;
usb_buf[0x01] = 0x03;
usb_buf[0x02] = ALIENWARE_COMMAND_DIM;
usb_buf[0x03] = static_cast<uint8_t>(percent);
usb_buf[0x04] = num_zones >> 8;
usb_buf[0x05] = num_zones & 0xFF;
for(size_t i = 0; i < num_zones; i++)
{
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
/*-------------------------------------------------*\
| Zero out buffer |
\*-------------------------------------------------*/
memset(usb_buf, 0x00, sizeof(usb_buf));
/*-------------------------------------------------*\
| Set up message packet with leading 00, per hidapi |
\*-------------------------------------------------*/
uint16_t num_zones = (uint16_t)std::min<size_t>(ALIENWARE_MAX_ZONES_PER_PACKET, zones.size() - offset);
usb_buf[0x00] = 0x00;
usb_buf[0x01] = 0x03;
usb_buf[0x02] = ALIENWARE_COMMAND_DIM;
usb_buf[0x03] = static_cast<uint8_t>(percent);
usb_buf[0x04] = num_zones >> 8;
usb_buf[0x05] = num_zones & 0xFF;
for(size_t i = 0; i < num_zones; i++)
{
usb_buf[0x06+i] = zones[offset + i];
}
/*-------------------------------------------------*\
| Send packet |
\*-------------------------------------------------*/
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
HidapiAlienwareReport response = GetResponse();
/*-------------------------------------------------*\
| For this command, error is if the output equals |
| the input |
\*-------------------------------------------------*/
result &= (response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE);
usb_buf[0x06+i] = zones[i];
}
return(result);
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
HidapiAlienwareReport response = GetResponse();
/*-----------------------------------------------------*\
| For this command, error is if the output equals the |
| input |
\*-----------------------------------------------------*/
return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE));
}
bool AlienwareController::UserAnimation(uint16_t subcommand, uint16_t animation, uint16_t duration)
@@ -410,53 +370,42 @@ bool AlienwareController::SelectZones(const std::vector<uint8_t>& zones)
return(false);
}
bool result = true;
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
/*-----------------------------------------------------*\
| Split the zone list into packet-sized chunks so it |
| never overflows usb_buf |
| Zero out buffer |
\*-----------------------------------------------------*/
for(size_t offset = 0; offset < zones.size(); offset += ALIENWARE_MAX_ZONES_PER_PACKET)
memset(usb_buf, 0x00, sizeof(usb_buf));
/*-----------------------------------------------------*\
| Set up message packet with leading 00, per hidapi |
\*-----------------------------------------------------*/
uint16_t num_zones = (uint16_t)zones.size();
usb_buf[0x00] = 0x00;
usb_buf[0x01] = 0x03;
usb_buf[0x02] = ALIENWARE_COMMAND_SELECT_ZONES;
usb_buf[0x03] = 1; // loop?
usb_buf[0x04] = num_zones >> 8;
usb_buf[0x05] = num_zones & 0xFF;
for(size_t i = 0; i < num_zones; i++)
{
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
/*-------------------------------------------------*\
| Zero out buffer |
\*-------------------------------------------------*/
memset(usb_buf, 0x00, sizeof(usb_buf));
/*-------------------------------------------------*\
| Set up message packet with leading 00, per hidapi |
\*-------------------------------------------------*/
uint16_t num_zones = (uint16_t)std::min<size_t>(ALIENWARE_MAX_ZONES_PER_PACKET, zones.size() - offset);
usb_buf[0x00] = 0x00;
usb_buf[0x01] = 0x03;
usb_buf[0x02] = ALIENWARE_COMMAND_SELECT_ZONES;
usb_buf[0x03] = 1; // loop?
usb_buf[0x04] = num_zones >> 8;
usb_buf[0x05] = num_zones & 0xFF;
for(size_t i = 0; i < num_zones; i++)
{
usb_buf[0x06+i] = zones[offset + i];
}
/*-------------------------------------------------*\
| Send packet |
\*-------------------------------------------------*/
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
HidapiAlienwareReport response = GetResponse();
/*-------------------------------------------------*\
| For this command, error is if the output equals |
| the input |
\*-------------------------------------------------*/
result &= (response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE);
usb_buf[0x06+i] = zones[i];
}
return(result);
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
HidapiAlienwareReport response = GetResponse();
/*-----------------------------------------------------*\
| For this command, error is if the output equals the |
| input |
\*-----------------------------------------------------*/
return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE));
}
bool AlienwareController::ModeAction(uint8_t mode, uint16_t duration, uint16_t tempo, RGBColor color)
@@ -560,55 +509,44 @@ bool AlienwareController::SetColorDirect(RGBColor color, std::vector<uint8_t> zo
return(true);
}
bool result = true;
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
/*-----------------------------------------------------*\
| Split the zone list into packet-sized chunks so it |
| never overflows usb_buf |
| Zero out buffer |
\*-----------------------------------------------------*/
for(size_t offset = 0; offset < zones.size(); offset += ALIENWARE_MAX_ZONES_PER_PACKET)
memset(usb_buf, 0x00, sizeof(usb_buf));
/*-----------------------------------------------------*\
| Set up message packet with leading 00, per hidapi |
\*-----------------------------------------------------*/
uint16_t num_zones = (uint16_t)zones.size();
usb_buf[0x00] = 0x00;
usb_buf[0x01] = 0x03;
usb_buf[0x02] = ALIENWARE_COMMAND_SET_COLOR;
usb_buf[0x03] = RGBGetRValue(color);
usb_buf[0x04] = RGBGetGValue(color);
usb_buf[0x05] = RGBGetBValue(color);
usb_buf[0x06] = num_zones >> 8;
usb_buf[0x07] = num_zones & 0xFF;
for(size_t i = 0; i < num_zones; i++)
{
unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE];
/*-------------------------------------------------*\
| Zero out buffer |
\*-------------------------------------------------*/
memset(usb_buf, 0x00, sizeof(usb_buf));
/*-------------------------------------------------*\
| Set up message packet with leading 00, per hidapi |
\*-------------------------------------------------*/
uint16_t num_zones = (uint16_t)std::min<size_t>(ALIENWARE_MAX_ZONES_PER_PACKET, zones.size() - offset);
usb_buf[0x00] = 0x00;
usb_buf[0x01] = 0x03;
usb_buf[0x02] = ALIENWARE_COMMAND_SET_COLOR;
usb_buf[0x03] = RGBGetRValue(color);
usb_buf[0x04] = RGBGetGValue(color);
usb_buf[0x05] = RGBGetBValue(color);
usb_buf[0x06] = num_zones >> 8;
usb_buf[0x07] = num_zones & 0xFF;
for(size_t i = 0; i < num_zones; i++)
{
usb_buf[0x08 + i] = zones[offset + i];
}
/*-------------------------------------------------*\
| Send packet |
\*-------------------------------------------------*/
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
HidapiAlienwareReport response = GetResponse();
/*-------------------------------------------------*\
| For this command, error is if the output equals |
| the input |
\*-------------------------------------------------*/
result &= (response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE);
usb_buf[0x08 + i] = zones[i];
}
return(result);
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
SendHIDReport(dev, usb_buf, sizeof(usb_buf));
HidapiAlienwareReport response = GetResponse();
/*-----------------------------------------------------*\
| For this command, error is if the output equals the |
| input |
\*-----------------------------------------------------*/
return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE));
}
bool AlienwareController::Reset()
@@ -702,15 +640,6 @@ void AlienwareController::SetDim(uint8_t zone, uint8_t dim)
}
}
void AlienwareController::SetDirection(uint8_t zone, uint8_t direction)
{
if(zone < zones.size())
{
zones[zone].direction = direction;
dirty = true;
}
}
void AlienwareController::UpdateDim()
{
if(!dirty_dim)
@@ -726,8 +655,7 @@ void AlienwareController::UpdateDim()
for(size_t i = 0; i < zones.size(); i++)
{
uint8_t hw_zone = (i < zone_ids.size()) ? zone_ids[i] : static_cast<uint8_t>(i);
dim_zone_map[zones[i].dim].emplace_back(hw_zone);
dim_zone_map[zones[i].dim].emplace_back((uint8_t)i);
}
for(std::pair<const uint8_t, std::vector<uint8_t>> &pair : dim_zone_map)
@@ -754,8 +682,7 @@ bool AlienwareController::UpdateDirect()
for(size_t i = 0; i < zones.size(); i++)
{
uint8_t hw_zone = (i < zone_ids.size()) ? zone_ids[i] : static_cast<uint8_t>(i);
color_zone_map[zones[i].color[0]].emplace_back(hw_zone);
color_zone_map[zones[i].color[0]].emplace_back((uint8_t)i);
}
for(std::pair<const RGBColor, std::vector<uint8_t>> &pair : color_zone_map)
@@ -799,9 +726,8 @@ void AlienwareController::UpdateMode()
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
alienware_zone zone = zones[zone_idx];
uint8_t hw_zone = (zone_idx < zone_ids.size()) ? zone_ids[zone_idx] : static_cast<uint8_t>(zone_idx);
result = SelectZones({hw_zone});
result = SelectZones({static_cast<uint8_t>(zone_idx)});
if(!result)
{
@@ -824,18 +750,18 @@ void AlienwareController::UpdateMode()
case ALIENWARE_MODE_MORPH:
{
uint8_t modes[2] = { zone.mode, zone.mode };
uint8_t zones[2] = { zone.mode, zone.mode };
uint16_t periods[2] = { zone.period, zone.period };
uint16_t tempos[2] = { zone.tempo, zone.tempo };
RGBColor colors[2] = { zone.color[0], zone.color[1] };
result = MultiModeAction(modes, periods, tempos, colors, 2);
result = MultiModeAction(zones, periods, tempos, colors, 2);
}
break;
case ALIENWARE_MODE_SPECTRUM:
{
uint8_t modes[7] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
uint8_t zones[7] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH };
@@ -848,13 +774,13 @@ void AlienwareController::UpdateMode()
zone.tempo, zone.tempo,
zone.tempo };
result = MultiModeAction(modes, periods, tempos, rainbow_colors[0], 7);
result = MultiModeAction(zones, periods, tempos, rainbow_colors[0], 7);
}
break;
case ALIENWARE_MODE_RAINBOW:
{
uint8_t modes[7] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
uint8_t zones[7] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH };
@@ -867,19 +793,18 @@ void AlienwareController::UpdateMode()
zone.tempo, zone.tempo,
zone.tempo };
unsigned int color_idx = (zone.direction == MODE_DIRECTION_LEFT) ? (4 - 1 - (zone_idx % 4)) : (zone_idx % 4);
result = MultiModeAction(modes, periods, tempos, rainbow_colors[color_idx], 7);
result = MultiModeAction(zones, periods, tempos, rainbow_colors[zone_idx], 7);
}
break;
case ALIENWARE_MODE_BREATHING:
{
uint8_t modes[2] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH };
uint8_t zones[2] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH };
uint16_t periods[2] = { zone.period, zone.period };
uint16_t tempos[2] = { zone.tempo, zone.tempo };
RGBColor colors[2] = { zone.color[0], 0x0 };
result = MultiModeAction(modes, periods, tempos, colors, 2);
result = MultiModeAction(zones, periods, tempos, colors, 2);
}
break;
@@ -906,117 +831,6 @@ void AlienwareController::UpdateMode()
dirty = false;
}
void AlienwareController::SaveController()
{
/*-----------------------------------------------------*\
| Save current mode configuration to EEPROM slot 0x0061 |
\*-----------------------------------------------------*/
UserAnimation(ALIENWARE_COMMAND_USER_ANIM_REMOVE, ALIENWARE_ANIM_DEFAULT, 0);
bool result = UserAnimation(ALIENWARE_COMMAND_USER_ANIM_NEW, ALIENWARE_ANIM_DEFAULT, 0);
if(!result)
{
return;
}
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
alienware_zone zone = zones[zone_idx];
uint8_t hw_zone = (zone_idx < zone_ids.size()) ? zone_ids[zone_idx] : static_cast<uint8_t>(zone_idx);
result = SelectZones({hw_zone});
if(!result)
{
return;
}
switch (zone.mode)
{
case ALIENWARE_MODE_COLOR:
result = ModeAction(zone.mode, 2000, ALIENWARE_TEMPO_MAX, zone.color[0]);
break;
case ALIENWARE_MODE_PULSE:
result = ModeAction(zone.mode, zone.period, zone.tempo, zone.color[0]);
break;
case ALIENWARE_MODE_MORPH:
{
uint8_t modes[2] = { zone.mode, zone.mode };
uint16_t periods[2] = { zone.period, zone.period };
uint16_t tempos[2] = { zone.tempo, zone.tempo };
RGBColor colors[2] = { zone.color[0], zone.color[1] };
result = MultiModeAction(modes, periods, tempos, colors, 2);
}
break;
case ALIENWARE_MODE_SPECTRUM:
{
uint8_t modes[7] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH };
uint16_t periods[7] = { zone.period, zone.period,
zone.period, zone.period,
zone.period, zone.period,
zone.period };
uint16_t tempos[7] = { zone.tempo, zone.tempo,
zone.tempo, zone.tempo,
zone.tempo, zone.tempo,
zone.tempo };
result = MultiModeAction(modes, periods, tempos, rainbow_colors[0], 7);
}
break;
case ALIENWARE_MODE_RAINBOW:
{
uint8_t modes[7] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH,
ALIENWARE_MODE_MORPH };
uint16_t periods[7] = { zone.period, zone.period,
zone.period, zone.period,
zone.period, zone.period,
zone.period };
uint16_t tempos[7] = { zone.tempo, zone.tempo,
zone.tempo, zone.tempo,
zone.tempo, zone.tempo,
zone.tempo };
unsigned int color_idx = (zone.direction == MODE_DIRECTION_LEFT) ? (4 - 1 - (zone_idx % 4)) : (zone_idx % 4);
result = MultiModeAction(modes, periods, tempos, rainbow_colors[color_idx], 7);
}
break;
case ALIENWARE_MODE_BREATHING:
{
uint8_t modes[2] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH };
uint16_t periods[2] = { zone.period, zone.period };
uint16_t tempos[2] = { zone.tempo, zone.tempo };
RGBColor colors[2] = { zone.color[0], 0x0 };
result = MultiModeAction(modes, periods, tempos, colors, 2);
}
break;
default:
result = false;
}
if(!result)
{
return;
}
}
UserAnimation(ALIENWARE_COMMAND_USER_ANIM_FINISH_SAVE, ALIENWARE_ANIM_DEFAULT, 0);
UserAnimation(ALIENWARE_COMMAND_USER_ANIM_DEFAULT, ALIENWARE_ANIM_DEFAULT, 0);
UserAnimation(ALIENWARE_COMMAND_USER_ANIM_STARTUP, ALIENWARE_ANIM_DEFAULT, 0);
}
void AlienwareController::UpdateController()
{
UpdateMode();
@@ -22,14 +22,6 @@
#define ALIENWARE_REPORT_SIZE 33
#define HIDAPI_ALIENWARE_REPORT_SIZE (ALIENWARE_REPORT_SIZE + 1)
#define ALIENWARE_CONTROLLER_NAME "AlienWare Controller"
/*---------------------------------------------------------*\
| Max zone IDs that fit in one report after the command |
| header. Selections larger than this must be split across |
| multiple packets; writing them into a single report |
| overflows usb_buf (e.g. desktops with ~100 zones). |
\*---------------------------------------------------------*/
#define ALIENWARE_MAX_ZONES_PER_PACKET 25
enum
{
ALIENWARE_COMMAND_REPORT = 0x20, /* Set report type to get */
@@ -123,13 +115,11 @@ public:
void SetPeriod(uint8_t zone, uint16_t period);
void SetTempo(uint8_t zone, uint16_t tempo);
void SetDim(uint8_t zone, uint8_t dim);
void SetDirection(uint8_t zone, uint8_t direction);
AlienwareReport GetStatus(uint8_t subcommand);
void UpdateDim();
void UpdateMode();
void UpdateController();
void SaveController();
protected:
hid_device* dev;
@@ -142,7 +132,6 @@ private:
uint16_t period;
uint16_t tempo;
uint8_t dim;
uint8_t direction;
} alienware_zone;
typedef struct
@@ -156,7 +145,6 @@ private:
std::string serial_number;
std::string version;
std::vector<const char*> zone_names;
std::vector<uint8_t> zone_ids;
bool dirty;
bool dirty_dim;
@@ -39,7 +39,7 @@ RGBController_Alienware::RGBController_Alienware(AlienwareController* controller
mode Color;
Color.name = "Static";
Color.value = ALIENWARE_MODE_COLOR;
Color.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Color.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Color.color_mode = MODE_COLORS_PER_LED;
Color.colors_min = 1;
Color.colors_max = 1;
@@ -51,13 +51,13 @@ RGBController_Alienware::RGBController_Alienware(AlienwareController* controller
mode Pulse;
Pulse.name = "Flashing";
Pulse.value = ALIENWARE_MODE_PULSE;
Pulse.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE;
Pulse.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Pulse.color_mode = MODE_COLORS_PER_LED;
Pulse.colors_min = 1;
Pulse.colors_max = 1;
Pulse.speed_min = ALIENWARE_TEMPO_MAX;
Pulse.speed_max = 50;
Pulse.speed = 150;
Pulse.speed_min = ALIENWARE_TEMPO_MIN;
Pulse.speed_max = ALIENWARE_TEMPO_MAX;
Pulse.speed = ALIENWARE_TEMPO_MIN;
Pulse.brightness_min = 100;
Pulse.brightness_max = 0;
Pulse.brightness = 0;
@@ -66,16 +66,14 @@ RGBController_Alienware::RGBController_Alienware(AlienwareController* controller
mode Morph;
Morph.name = "Morph";
Morph.value = ALIENWARE_MODE_MORPH;
Morph.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE;
Morph.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Morph.color_mode = MODE_COLORS_MODE_SPECIFIC;
Morph.colors_min = 2;
Morph.colors_max = 2 * controller->GetZoneCount();
Morph.colors.resize(2);
Morph.colors[0] = 0x0000FF;
Morph.colors[1] = 0xFF0000;
Morph.speed_min = ALIENWARE_TEMPO_MAX;
Morph.speed_max = 20;
Morph.speed = 100;
Morph.colors_min = 2 * controller->GetZoneCount();
Morph.colors_max = Morph.colors_min;
Morph.colors.resize(Morph.colors_max);
Morph.speed_min = ALIENWARE_TEMPO_MIN;
Morph.speed_max = ALIENWARE_TEMPO_MAX;
Morph.speed = ALIENWARE_TEMPO_MIN;
Morph.brightness_min = 100;
Morph.brightness_max = 0;
Morph.brightness = 0;
@@ -84,11 +82,11 @@ RGBController_Alienware::RGBController_Alienware(AlienwareController* controller
mode Spectrum;
Spectrum.name = "Spectrum Cycle";
Spectrum.value = ALIENWARE_MODE_SPECTRUM;
Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE;
Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Spectrum.color_mode = MODE_COLORS_NONE;
Spectrum.speed_min = ALIENWARE_TEMPO_MAX;
Spectrum.speed_max = ALIENWARE_TEMPO_SPECTRUM;
Spectrum.speed = 40;
Spectrum.speed_min = ALIENWARE_TEMPO_SPECTRUM;
Spectrum.speed_max = ALIENWARE_TEMPO_MAX;
Spectrum.speed = ALIENWARE_TEMPO_SPECTRUM;
Spectrum.brightness_min = 100;
Spectrum.brightness_max = 0;
Spectrum.brightness = 0;
@@ -97,12 +95,11 @@ RGBController_Alienware::RGBController_Alienware(AlienwareController* controller
mode Rainbow;
Rainbow.name = "Rainbow Wave";
Rainbow.value = ALIENWARE_MODE_RAINBOW;
Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_MANUAL_SAVE;
Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Rainbow.color_mode = MODE_COLORS_NONE;
Rainbow.speed_min = ALIENWARE_TEMPO_MAX;
Rainbow.speed_max = ALIENWARE_TEMPO_SPECTRUM;
Rainbow.speed = 40;
Rainbow.direction = MODE_DIRECTION_RIGHT;
Rainbow.speed_min = ALIENWARE_TEMPO_SPECTRUM;
Rainbow.speed_max = ALIENWARE_TEMPO_MAX;
Rainbow.speed = ALIENWARE_TEMPO_SPECTRUM;
Rainbow.brightness_min = 100;
Rainbow.brightness_max = 0;
Rainbow.brightness = 0;
@@ -111,13 +108,13 @@ RGBController_Alienware::RGBController_Alienware(AlienwareController* controller
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = ALIENWARE_MODE_BREATHING;
Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE;
Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Breathing.color_mode = MODE_COLORS_PER_LED;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.speed_min = ALIENWARE_TEMPO_MAX;
Breathing.speed_max = 50;
Breathing.speed = 150;
Breathing.speed_min = ALIENWARE_TEMPO_MIN;
Breathing.speed_max = ALIENWARE_TEMPO_MAX;
Breathing.speed = ALIENWARE_TEMPO_MIN;
Breathing.brightness_min = 100;
Breathing.brightness_max = 0;
Breathing.brightness = 0;
@@ -227,7 +224,6 @@ void RGBController_Alienware::DeviceUpdateMode()
uint16_t period = 0x07d0;
controller->SetMode(zone_idx, current_mode.value);
controller->SetDirection(zone_idx, current_mode.direction);
switch(current_mode_idx)
{
@@ -239,7 +235,6 @@ void RGBController_Alienware::DeviceUpdateMode()
break;
case ALIENWARE_MODE_PULSE:
period = std::max<uint16_t>(500, (uint16_t)(current_mode.speed * 10));
controller->SetPeriod(zone_idx, period);
controller->SetColor( zone_idx, colors[current_zone.start_idx]);
controller->SetTempo( zone_idx, current_mode.speed);
@@ -247,22 +242,10 @@ void RGBController_Alienware::DeviceUpdateMode()
break;
case ALIENWARE_MODE_MORPH:
{
RGBColor c1 = (current_mode.colors.size() >= 1) ? current_mode.colors[0] : 0x0000FF;
RGBColor c2 = (current_mode.colors.size() >= 2) ? current_mode.colors[1] : 0xFF0000;
if(current_mode.colors.size() >= (std::size_t)((zone_idx + 1) * 2))
{
c1 = current_mode.colors[zone_idx * 2];
c2 = current_mode.colors[(zone_idx * 2) + 1];
}
period = std::max<uint16_t>(400, (uint16_t)(current_mode.speed * 10));
controller->SetPeriod(zone_idx, period);
controller->SetColor( zone_idx, c1, c2);
controller->SetTempo( zone_idx, current_mode.speed);
controller->SetDim( zone_idx, modes[current_mode_idx].brightness);
}
controller->SetPeriod(zone_idx, period);
controller->SetColor( zone_idx, current_mode.colors[zone_idx * 2], current_mode.colors[(zone_idx * 2) + 1]);
controller->SetTempo( zone_idx, current_mode.speed);
controller->SetDim( zone_idx, modes[current_mode_idx].brightness);
break;
case ALIENWARE_MODE_SPECTRUM:
@@ -273,7 +256,6 @@ void RGBController_Alienware::DeviceUpdateMode()
break;
case ALIENWARE_MODE_BREATHING:
period = std::max<uint16_t>(500, (uint16_t)(current_mode.speed * 10));
controller->SetPeriod(zone_idx, period);
controller->SetColor( zone_idx, colors[current_zone.start_idx], 0x0);
controller->SetTempo( zone_idx, current_mode.speed);
@@ -300,9 +282,3 @@ void RGBController_Alienware::DeviceUpdateMode()
current_mode = new_current_mode;
}
}
void RGBController_Alienware::DeviceSaveMode()
{
controller->SaveController();
}
@@ -28,7 +28,6 @@ public:
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
AlienwareController* controller;
@@ -40,11 +40,11 @@ std::vector<unsigned char> AlienwareAW3423DWFController::GetReportResponse()
void AlienwareAW3423DWFController::PerformLogin()
{
unsigned char init_packet[192] = {0x00};
init_packet[0] = 0x40;
init_packet[1] = 0xE1;
init_packet[2] = 0x01;
SendControlPacket(init_packet, 192);
unsigned char init_packet[64] =
{
0x40, 0xE1, 0x01
};
SendControlPacket(init_packet, 4);
std::vector<unsigned char> response = GetReportResponse();
@@ -1,47 +0,0 @@
/*---------------------------------------------------------*\
| AlienwareAW3423DWFControllerDetect.cpp |
| |
| Detector for Alienware AW3423DWF monitor |
| |
| Ferréol DUBOIS COLI (Fefe_du_973) 23 Jan 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "AlienwareAW3423DWFController.h"
#include "DetectionManager.h"
#include "RGBController_AlienwareAW3423DWF.h"
/*---------------------------------------------------------*\
| Alienware Vendor ID |
\*---------------------------------------------------------*/
#define ALIENWARE_VID 0x187C
/*---------------------------------------------------------*\
| Alienware Vendor ID |
\*---------------------------------------------------------*/
#define ALIENWARE_AW3423DWF_PID 0x100E
#define ALIENWARE_USAGE_PAGE 0xFFDA
#define ALIENWARE_USAGE 0x00DA
DetectedControllers DetectAlienwareAW3423DWFControllers(hid_device_info* info, const std::string&)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
AlienwareAW3423DWFController* controller = new AlienwareAW3423DWFController(dev, info->path);
RGBController_AlienwareAW3423DWF* rgb_controller = new RGBController_AlienwareAW3423DWF(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR("Alienware AW3423DWF", DetectAlienwareAW3423DWFControllers, ALIENWARE_VID, ALIENWARE_AW3423DWF_PID);
@@ -10,8 +10,10 @@
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "AlienwareAW3423DWFController.h"
#include "AlienwareMonitorController.h"
#include "DetectionManager.h"
#include "RGBController_AlienwareAW3423DWF.h"
#include "RGBController_AlienwareMonitor.h"
/*---------------------------------------------------------*\
@@ -22,10 +24,29 @@
/*---------------------------------------------------------*\
| Alienware Vendor ID |
\*---------------------------------------------------------*/
#define ALIENWARE_AW3423DWF_PID 0x100E
#define ALIENWARE_AW3225QF_PID 0x1013
#define ALIENWARE_USAGE_PAGE 0xFFDA
#define ALIENWARE_USAGE 0x00DA
DetectedControllers DetectAlienwareAW3423DWFControllers(hid_device_info* info, const std::string&)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
AlienwareAW3423DWFController* controller = new AlienwareAW3423DWFController(dev, info->path);
RGBController_AlienwareAW3423DWF* rgb_controller = new RGBController_AlienwareAW3423DWF(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
DetectedControllers DetectAlienwareMonitorControllers(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
@@ -44,4 +65,5 @@ DetectedControllers DetectAlienwareMonitorControllers(hid_device_info* info, con
return(detected_controllers);
}
REGISTER_HID_DETECTOR("Alienware AW3423DWF", DetectAlienwareAW3423DWFControllers, ALIENWARE_VID, ALIENWARE_AW3423DWF_PID);
REGISTER_HID_DETECTOR("Alienware AW3225QF", DetectAlienwareMonitorControllers, ALIENWARE_VID, ALIENWARE_AW3225QF_PID);
@@ -44,4 +44,8 @@ DetectedControllers DetectArcticControllers()
}
REGISTER_DETECTOR("Arctic RGB controller", DetectArcticControllers);
REGISTER_CUSTOM_UDEV_RULE(arctic, "Arctic RGB controller", "SUBSYSTEMS==\"serial|hidraw\", ATTRS{idVendor}==\"1a86\", ATTRS{idProduct}==\"7523\", TAG+=\"uaccess\", TAG+=\"Arctic_RGB_controller\"");
/*---------------------------------------------------------------------------------------------------------*\
| Entries for dynamic UDEV rules |
| |
| DUMMY_DEVICE_DETECTOR("Arctic RGB controller", DetectArcticControllers, 0x1A86, 0x7523 ) |
\*---------------------------------------------------------------------------------------------------------*/
@@ -4,15 +4,18 @@
| Detector for ASUS ROG Aura Core |
| |
| Adam Honse (CalcProgrammer1) 13 Apr 2020 |
| Chris M (Dr_No) 28 Jul 2022 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "AsusAuraCoreController.h"
#include "DetectionManager.h"
#include "AsusAuraCoreController.h"
#include "RGBController.h"
#include "RGBController_AsusAuraCore.h"
#include "RGBController_AsusAuraCoreLaptop.h"
#include <hidapi.h>
#define AURA_CORE_VID 0x0B05
@@ -41,6 +44,27 @@ DetectedControllers DetectAsusAuraCoreControllers(hid_device_info* info, const s
return(detected_controllers);
}
DetectedControllers DetectAsusAuraCoreLaptopControllers(hid_device_info* info, const std::string& /*name*/)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
AsusAuraCoreLaptopController* controller = new AsusAuraCoreLaptopController(dev, info->path);
RGBController_AsusAuraCoreLaptop* rgb_controller = new RGBController_AsusAuraCoreLaptop(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR ("ASUS Aura Core", DetectAsusAuraCoreControllers, AURA_CORE_VID, 0x1854);
REGISTER_HID_DETECTOR ("ASUS Aura Core", DetectAsusAuraCoreControllers, AURA_CORE_VID, 0x1866);
REGISTER_HID_DETECTOR ("ASUS Aura Core", DetectAsusAuraCoreControllers, AURA_CORE_VID, 0x1869);
REGISTER_HID_DETECTOR_PU("ASUS ROG Strix SCAR 15", DetectAsusAuraCoreLaptopControllers, AURA_CORE_VID, AURA_STRIX_SCAR_15_PID, 0xFF31, 0x79);
REGISTER_HID_DETECTOR_PU("ASUS ROG Strix SCAR 17", DetectAsusAuraCoreLaptopControllers, AURA_CORE_VID, 0x1866, 0xFF31, 0x79);
@@ -1,38 +0,0 @@
/*---------------------------------------------------------*\
| AsusAuraCoreLaptopControllerDetect.cpp |
| |
| Detector for ASUS ROG Aura Core Laptop |
| |
| Chris M (Dr_No) 28 Jul 2022 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "AsusAuraCoreLaptopController.h"
#include "DetectionManager.h"
#include "RGBController_AsusAuraCoreLaptop.h"
#define AURA_CORE_VID 0x0B05
DetectedControllers DetectAsusAuraCoreLaptopControllers(hid_device_info* info, const std::string& /*name*/)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
AsusAuraCoreLaptopController* controller = new AsusAuraCoreLaptopController(dev, info->path);
RGBController_AsusAuraCoreLaptop* rgb_controller = new RGBController_AsusAuraCoreLaptop(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_PU("ASUS ROG Strix SCAR 15", DetectAsusAuraCoreLaptopControllers, AURA_CORE_VID, AURA_STRIX_SCAR_15_PID, 0xFF31, 0x79);
REGISTER_HID_DETECTOR_PU("ASUS ROG Strix SCAR 17", DetectAsusAuraCoreLaptopControllers, AURA_CORE_VID, 0x1866, 0xFF31, 0x79);
@@ -940,25 +940,6 @@ static const aura_core_laptop_device g814jv_device =
}
};
/*-------------------------------------------------------------*\
| ASUS ROG Strix G18 (G814JIR) |
| |
| G814JIR uses the same Aura Core keyboard and lightbar |
| layout as the G814JV G18 profile. |
\*-------------------------------------------------------------*/
static const aura_core_laptop_device g814jir_device =
{
"G814JIR",
{
&g814rw_keyboard_zone,
&g814jv_lightbar_zone,
nullptr,
nullptr,
nullptr,
nullptr
}
};
/*-------------------------------------------------------------------------*\
| DEVICE MASTER LIST |
\*-------------------------------------------------------------------------*/
@@ -973,7 +954,6 @@ const aura_core_laptop_device* aura_core_laptop_device_list_data[] =
&g713rw_device,
&g733qr_device,
&g814jv_device,
&g814jir_device,
};
const unsigned int AURA_CORE_LAPTOP_DEVICE_COUNT = (sizeof(aura_core_laptop_device_list_data) / sizeof(aura_core_laptop_device_list_data[ 0 ]));
@@ -428,6 +428,7 @@ void RGBController_AuraTUFKeyboard::SetupZones()
break;
case AURA_TUF_K3_GAMING_PID:
case AURA_TUF_K7_GAMING_PID:
case AURA_TUF_K3_GENII_MIKU_EDITION_PID:
keyboard_ptr = &AsusTUFK7Layouts;
break;
case AURA_ROG_STRIX_SCOPE_PID:
@@ -484,7 +485,6 @@ void RGBController_AuraTUFKeyboard::SetupZones()
keyboard_ptr = &AsusTufK1Layouts;
break;
case AURA_TUF_K3_GAMING_GEN_II_PID:
case AURA_TUF_K3_GENII_MIKU_EDITION_PID:
keyboard_ptr = &AsusTUFK3GamingGen2Layouts;
break;
default:
@@ -417,7 +417,7 @@ REGISTER_HID_DETECTOR ("ASUS Aura Addressable", DetectAs
REGISTER_HID_DETECTOR ("ASUS Aura Addressable", DetectAsusAuraUSBAddressable, AURA_USB_VID, AURA_ADDRESSABLE_4_PID);
REGISTER_HID_DETECTOR ("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_1_PID);
REGISTER_HID_DETECTOR ("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_2_PID);
REGISTER_HID_DETECTOR_PU("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_3_PID, 0xFF72, 0x00A1);
REGISTER_HID_DETECTOR ("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_3_PID);
REGISTER_HID_DETECTOR ("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_4_PID);
REGISTER_HID_DETECTOR ("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_5_PID);
@@ -22,12 +22,8 @@
| Product ID |
\*---------------------------------------------------------*/
#define ASUS_ROG_STRIX_XG27AQDMG_PID 0x1BA3
#define ASUS_ROG_STRIX_XG27ACDNG_PID 0x1BC9
#define ASUS_ROG_STRIX_XG27UCG_PID 0x1BB4
#define ASUS_ROG_SWIFT_XG27UCG_PID 0x1BB4
#define ASUS_ROG_SWIFT_PG32UCDM_PID 0x1B2B
#define ASUS_ROG_SWIFT_PG32UCDMR_PID 0x1C9B
#define ASUS_ROG_SWIFT_PG32UCDP_PID 0x1BCA
#define ASUS_ROG_AURA_MONITOR_LIGHT_BAR_PID 0x1AC8
DetectedControllers DetectAsusMonitorControllers(hid_device_info* info, const std::string& name)
{
@@ -48,13 +44,5 @@ DetectedControllers DetectAsusMonitorControllers(hid_device_info* info, const st
}
REGISTER_HID_DETECTOR_IPU("Asus ROG STRIX XG27AQDMG", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_STRIX_XG27AQDMG_PID, 1, 0xFF72, 0x00A1);
REGISTER_HID_DETECTOR_IPU("Asus ROG STRIX XG27ACDNG", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_STRIX_XG27ACDNG_PID, 1, 0xFF72, 0x00A1);
REGISTER_HID_DETECTOR_IPU("Asus ROG STRIX XG27UCG", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_STRIX_XG27UCG_PID, 1, 0xFF72, 0x00A1);
REGISTER_HID_DETECTOR_IPU("Asus ROG SWIFT PG32UCDM", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_SWIFT_PG32UCDM_PID, 1, 0xFF72, 0x00A1);
REGISTER_HID_DETECTOR_IPU("Asus ROG SWIFT PG32UCDMR", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_SWIFT_PG32UCDMR_PID, 1, 0xFF72, 0x00A1);
REGISTER_HID_DETECTOR_IPU("Asus ROG SWIFT PG32UCDP", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_SWIFT_PG32UCDP_PID, 1, 0xFF72, 0x00A1);
/*---------------------------------------------------------*\
| ALB01 exposes the Aura HID collection on interface 2. |
\*---------------------------------------------------------*/
REGISTER_HID_DETECTOR_IPU("Asus ROG Aura Monitor Light Bar", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_AURA_MONITOR_LIGHT_BAR_PID, 2, 0xFF72, 0x00A1);
REGISTER_HID_DETECTOR_IPU("Asus ROG STRIX XG27UCG", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_SWIFT_XG27UCG_PID, 1, 0xFF72, 0x00A1);
REGISTER_HID_DETECTOR_IPU("Asus ROG STRIX PG32UCDM", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_SWIFT_PG32UCDM_PID, 1, 0xFF72, 0x00A1);
@@ -36,5 +36,3 @@ DetectedControllers DetectAsusTUFLaptopLinuxControllers()
}
REGISTER_DETECTOR("ASUS TUF Laptop", DetectAsusTUFLaptopLinuxControllers);
REGISTER_CUSTOM_UDEV_RULE(asus_wmi, "ASUS TUF Laptops (asus-wmi)", "ACTION==\"add\", SUBSYSTEM==\"leds\", KERNEL==\"asus::kbd_backlight\", RUN+=\"/usr/bin/env chmod a+w /sys%p/kbd_rgb_mode\"\nACTION==\"add\", SUBSYSTEM==\"leds\", KERNEL==\"asus::kbd_backlight\", RUN+=\"/usr/bin/env chmod a+w /sys%p/brightness\"");
@@ -40,4 +40,8 @@ DetectedControllers DetectBlinkyTapeControllers()
}
REGISTER_DETECTOR("BlinkyTape", DetectBlinkyTapeControllers);
REGISTER_CUSTOM_UDEV_RULE(blinky_tape, "BlinkyTape", "SUBSYSTEMS==\"serial|hidraw\", ATTRS{idVendor}==\"1d50\", ATTRS{idProduct}==\"605e\", TAG+=\"uaccess\", TAG+=\"BlinkyTape\"");
/*---------------------------------------------------------------------------------------------------------*\
| Entries for dynamic UDEV rules |
| |
| DUMMY_DEVICE_DETECTOR("BlinkyTape", DetectBlinkyTapeControllers, 0x1D50, 0x605E ) |
\*---------------------------------------------------------------------------------------------------------*/
@@ -1,25 +1,22 @@
/*---------------------------------------------------------*\
| ClevoKeyboardController.cpp |
| |
| Driver for Clevo per-key RGB keyboard |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| Driver for Clevo laptop per-key RGB keyboard (ITE 8291) |
| Protocol based on tuxedo-drivers ite_8291 module |
| |
| Kyle Cascade ([email protected]) 16 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <algorithm>
#include <cstring>
#include "ClevoKeyboardController.h"
#include "StringUtils.h"
ClevoKeyboardController::ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info, clevo_keyboard_type kb_type)
ClevoKeyboardController::ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info)
{
dev = dev_handle;
type = kb_type;
location = info.path;
version = info.release_number;
}
@@ -29,31 +26,6 @@ ClevoKeyboardController::~ClevoKeyboardController()
hid_close(dev);
}
clevo_keyboard_type ClevoKeyboardController::GetType()
{
return type;
}
int ClevoKeyboardController::GetNumRows()
{
return(type == CLEVO_KB_ITE829X) ? CLEVO_829X_NUM_ROWS : CLEVO_8291_NUM_ROWS;
}
int ClevoKeyboardController::GetNumCols()
{
return(type == CLEVO_KB_ITE829X) ? CLEVO_829X_NUM_COLS : CLEVO_8291_NUM_COLS;
}
int ClevoKeyboardController::GetNumLEDs()
{
return GetNumRows() * GetNumCols();
}
unsigned char ClevoKeyboardController::GetBrightnessMax()
{
return(type == CLEVO_KB_ITE829X) ? CLEVO_829X_BRIGHTNESS_MAX : CLEVO_8291_BRIGHTNESS_MAX;
}
std::string ClevoKeyboardController::GetDeviceLocation()
{
return("HID: " + location);
@@ -79,355 +51,147 @@ std::string ClevoKeyboardController::GetFirmwareVersion()
return(version_string);
}
/*---------------------------------------------------------*\
| ITE 8291: 8-byte feature report, no report ID |
\*---------------------------------------------------------*/
void ClevoKeyboardController::WriteControl8291(unsigned char* data)
void ClevoKeyboardController::WriteControl(unsigned char* data)
{
hid_send_feature_report(dev, data, CLEVO_8291_REPORT_SIZE);
hid_send_feature_report(dev, data, CLEVO_KEYBOARD_REPORT_SIZE);
}
void ClevoKeyboardController::WriteRowData(unsigned char* data)
{
hid_write(dev, data, CLEVO_8291_ROW_DATA_SIZE);
}
/*---------------------------------------------------------*\
| ITE 829x: 6-byte feature report with report ID 0xCC |
\*---------------------------------------------------------*/
void ClevoKeyboardController::SendCommand829x(unsigned char cmd, unsigned char d0, unsigned char d1, unsigned char d2, unsigned char d3)
{
unsigned char buf[CLEVO_829X_REPORT_SIZE];
buf[0] = CLEVO_829X_REPORT_ID;
buf[1] = cmd;
buf[2] = d0;
buf[3] = d1;
buf[4] = d2;
buf[5] = d3;
hid_send_feature_report(dev, buf, CLEVO_829X_REPORT_SIZE);
hid_write(dev, data, CLEVO_KEYBOARD_ROW_DATA_SIZE);
}
void ClevoKeyboardController::TurnOff()
{
if(type == CLEVO_KB_ITE829X)
{
SendCommand829x(CLEVO_829X_CMD_BRIGHTNESS);
return;
}
/*---------------------------------------------------------*\
| Turn off: 08 01 00 00 00 00 00 00 |
\*---------------------------------------------------------*/
unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE];
unsigned char buf[CLEVO_8291_REPORT_SIZE];
memset(buf, 0x00, CLEVO_8291_REPORT_SIZE);
buf[0] = CLEVO_8291_CMD_MODE;
buf[1] = CLEVO_8291_POWER_OFF;
WriteControl8291(buf);
memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
buf[0] = 0x08;
buf[1] = 0x01;
WriteControl(buf);
}
void ClevoKeyboardController::SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char behaviour)
{
if(type == CLEVO_KB_ITE829X)
{
SendCommand829x(CLEVO_829X_CMD_BRIGHTNESS, brightness, speed);
return;
}
/*---------------------------------------------------------*\
| Set params: 08 [power] [mode] [speed] [brightness] 08 |
| [behaviour] 00 |
| power: 01=off, 02=on |
\*---------------------------------------------------------*/
unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE];
unsigned char buf[CLEVO_8291_REPORT_SIZE];
memset(buf, 0x00, CLEVO_8291_REPORT_SIZE);
buf[0] = CLEVO_8291_CMD_MODE;
buf[1] = CLEVO_8291_POWER_ON;
memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
buf[0] = 0x08;
buf[1] = 0x02; // Power on
buf[2] = mode;
buf[3] = speed;
buf[4] = brightness;
buf[5] = CLEVO_8291_MODE_FLAG;
buf[5] = 0x08;
buf[6] = behaviour;
WriteControl8291(buf);
WriteControl(buf);
}
void ClevoKeyboardController::SetModeColor(unsigned char color_idx, RGBColor color)
void ClevoKeyboardController::SetModeColor(unsigned char color_idx, unsigned char red, unsigned char green, unsigned char blue)
{
if(type == CLEVO_KB_ITE829X)
{
return;
}
/*---------------------------------------------------------*\
| Set color define: 14 00 [index] R G B 00 00 |
| index: 1-7 for built-in effects |
\*---------------------------------------------------------*/
unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE];
unsigned char buf[CLEVO_8291_REPORT_SIZE];
memset(buf, 0x00, CLEVO_8291_REPORT_SIZE);
buf[0] = CLEVO_8291_CMD_COLOR;
memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
buf[0] = 0x14;
buf[1] = 0x00;
buf[2] = color_idx;
buf[3] = RGBGetRValue(color);
buf[4] = RGBGetGValue(color);
buf[5] = RGBGetBValue(color);
WriteControl8291(buf);
buf[3] = red;
buf[4] = green;
buf[5] = blue;
WriteControl(buf);
}
void ClevoKeyboardController::SetBrightnessSpeed829x(unsigned char brightness, unsigned char speed)
void ClevoKeyboardController::SendColors(unsigned char* color_data, unsigned char brightness)
{
if(brightness > CLEVO_829X_BRIGHTNESS_MAX)
{
brightness = CLEVO_829X_BRIGHTNESS_MAX;
}
if(brightness != current_brightness_829x || speed != current_speed_829x)
{
current_brightness_829x = brightness;
current_speed_829x = speed;
SendCommand829x(CLEVO_829X_CMD_BRIGHTNESS, brightness, speed);
}
}
void ClevoKeyboardController::SendColorSlot829x(unsigned char cmd, unsigned char slot, RGBColor color)
{
SendCommand829x(cmd, slot, RGBGetRValue(color), RGBGetGValue(color), RGBGetBValue(color));
}
/*---------------------------------------------------------*\
| ITE 829x: Save/Clear BIOS keyboard state |
\*---------------------------------------------------------*/
void ClevoKeyboardController::SaveToBios829x()
{
SendCommand829x(CLEVO_829X_CMD_SAVE_BIOS, 0x01);
}
void ClevoKeyboardController::ClearBiosSave829x()
{
SendCommand829x(CLEVO_829X_CMD_SAVE_BIOS, 0x00);
}
void ClevoKeyboardController::StopAnimation829x()
{
SendCommand829x(CLEVO_829X_CMD_ANIM, CLEVO_829X_CMD_STOP_ANIM);
}
void ClevoKeyboardController::SetEffect829x(unsigned char mode_value, const std::vector<RGBColor>& colors, unsigned int direction, unsigned int color_mode)
{
bool mode_changed = (mode_value != current_mode_829x);
current_mode_829x = mode_value;
bool use_custom_color = (color_mode != MODE_COLORS_RANDOM) && !colors.empty();
/*---------------------------------------------------------*\
| Per-key RGB mode (mode 0x33) |
| 1. Set params with mode 0x33 and brightness |
| 2. For each row 0-5: |
| - Announce row: 16 00 [row] 00 00 00 00 00 |
| - Send 65 bytes row data via output report |
| |
| Row data format (65 bytes): |
| [0x00 padding][0x00 padding] |
| [B0..B20][G0..G20][R0..R20] |
\*---------------------------------------------------------*/
unsigned char ctrl_buf[CLEVO_KEYBOARD_REPORT_SIZE];
unsigned char row_buf[CLEVO_KEYBOARD_ROW_DATA_SIZE];
/*---------------------------------------------------------*\
| Animation modes: send [CC, 00, mode_id] on mode change |
| Clamp brightness |
\*---------------------------------------------------------*/
struct anim_entry { unsigned char mode; unsigned char anim_id; };
static const anim_entry anim_modes[] =
if(brightness > CLEVO_KEYBOARD_BRIGHTNESS_MAX)
{
{ CLEVO_829X_MODE_WAVE, 0x04 },
{ CLEVO_829X_MODE_RANDOM, 0x09 },
{ CLEVO_829X_MODE_SCAN, 0x0A },
{ CLEVO_829X_MODE_SNAKE, 0x0B },
{ CLEVO_829X_MODE_RAINBOW_CYCLE, 0x02 },
{ CLEVO_829X_MODE_RAINBOW_GRAD, 0x04 },
};
const anim_entry* anim_it = std::find_if(std::begin(anim_modes), std::end(anim_modes),
[mode_value](const anim_entry& e) { return e.mode == mode_value; });
if(anim_it != std::end(anim_modes) && mode_changed)
{
SendCommand829x(CLEVO_829X_CMD_ANIM, anim_it->anim_id);
brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
}
/*---------------------------------------------------------*\
| Direction-based effects (Wave/Snake) |
| Slot = base + direction_index |
| Custom color: base = 0xA1, Preset: base = 0x71 |
| Set params for per-key mode |
\*---------------------------------------------------------*/
/*---------------------------------------------------------*\
| Map OpenRGB direction enum to firmware slot index |
\*---------------------------------------------------------*/
static const unsigned char dir_to_wave_idx[] =
{
6, /* MODE_DIRECTION_LEFT */
7, /* MODE_DIRECTION_RIGHT */
4, /* MODE_DIRECTION_UP */
5, /* MODE_DIRECTION_DOWN */
6, /* MODE_DIRECTION_HORIZONTAL */
4, /* MODE_DIRECTION_VERTICAL */
0, /* MODE_DIRECTION_UP_LEFT */
1, /* MODE_DIRECTION_UP_RIGHT */
2, /* MODE_DIRECTION_DOWN_LEFT */
3, /* MODE_DIRECTION_DOWN_RIGHT */
};
static const unsigned char dir_to_snake_idx[] =
{
0, /* MODE_DIRECTION_LEFT */
0, /* MODE_DIRECTION_RIGHT */
0, /* MODE_DIRECTION_UP */
0, /* MODE_DIRECTION_DOWN */
0, /* MODE_DIRECTION_HORIZONTAL */
0, /* MODE_DIRECTION_VERTICAL */
0, /* MODE_DIRECTION_UP_LEFT */
1, /* MODE_DIRECTION_UP_RIGHT */
2, /* MODE_DIRECTION_DOWN_LEFT */
3, /* MODE_DIRECTION_DOWN_RIGHT */
};
if((mode_value == CLEVO_829X_MODE_WAVE || mode_value == CLEVO_829X_MODE_RAINBOW_GRAD) && direction < sizeof(dir_to_wave_idx))
{
unsigned char base = use_custom_color ? CLEVO_829X_COLOR_SLOT_BASE : CLEVO_829X_PRESET_SLOT_BASE;
unsigned char idx = dir_to_wave_idx[direction];
unsigned char slot = base + idx;
if(use_custom_color)
{
SendColorSlot829x(CLEVO_829X_CMD_WAVE_COLOR, slot, colors[0]);
}
if(!use_custom_color)
{
SendCommand829x(CLEVO_829X_CMD_WAVE_COLOR, slot);
}
}
if(mode_value == CLEVO_829X_MODE_SNAKE && direction < sizeof(dir_to_snake_idx))
{
unsigned char base = use_custom_color ? CLEVO_829X_COLOR_SLOT_BASE : CLEVO_829X_PRESET_SLOT_BASE;
unsigned char idx = dir_to_snake_idx[direction];
unsigned char slot = base + idx;
if(use_custom_color)
{
SendColorSlot829x(CLEVO_829X_CMD_SNAKE_COLOR, slot, colors[0]);
}
if(!use_custom_color)
{
SendCommand829x(CLEVO_829X_CMD_SNAKE_COLOR, slot);
}
}
/*---------------------------------------------------------*\
| Breathing/Flashing: random or custom color |
\*---------------------------------------------------------*/
if(mode_value == CLEVO_829X_MODE_BREATH && use_custom_color)
{
SendColorSlot829x(0x0A, CLEVO_829X_COLOR_CUSTOM, colors[0]);
}
if(mode_value == CLEVO_829X_MODE_BREATH && !use_custom_color)
{
SendCommand829x(0x0A);
}
if(mode_value == CLEVO_829X_MODE_BLINK && use_custom_color)
{
SendColorSlot829x(0x0B, CLEVO_829X_COLOR_CUSTOM, colors[0]);
}
if(mode_value == CLEVO_829X_MODE_BLINK && !use_custom_color)
{
SendCommand829x(0x0B);
}
/*---------------------------------------------------------*\
| Random: custom color only (multicolor is the default) |
\*---------------------------------------------------------*/
if(mode_value == CLEVO_829X_MODE_RANDOM && use_custom_color)
{
SendColorSlot829x(CLEVO_829X_CMD_RANDOM_COLOR, CLEVO_829X_COLOR_SLOT_BASE, colors[0]);
}
/*---------------------------------------------------------*\
| Scan: 2 custom color bands |
\*---------------------------------------------------------*/
if(mode_value == CLEVO_829X_MODE_SCAN)
{
for(size_t i = 0; i < colors.size() && i < 2; i++)
{
SendColorSlot829x(CLEVO_829X_CMD_SCAN_COLOR, CLEVO_829X_COLOR_SLOT_BASE + (unsigned char)i, colors[i]);
}
}
/*---------------------------------------------------------*\
| Fn Highlight: per-key color on function keys |
\*---------------------------------------------------------*/
if(mode_value == CLEVO_829X_MODE_FN_COLOR && !colors.empty())
{
static const unsigned char fn_keys[] =
{
0x01, 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0A, 0x0B, 0x0C, 0x21, 0x23, 0x31, 0x32, 0x33,
0x53, 0x73, 0xA2
};
SendCommand829x(CLEVO_829X_CMD_ANIM, CLEVO_829X_CMD_CLEAR);
for(unsigned char key : fn_keys)
{
SendCommand829x(CLEVO_829X_CMD_SET_LED, key,
RGBGetRValue(colors[0]),
RGBGetGValue(colors[0]),
RGBGetBValue(colors[0]));
}
}
}
void ClevoKeyboardController::SendColors(const unsigned char* color_data, unsigned char brightness, unsigned char speed)
{
int num_rows = GetNumRows();
int num_cols = GetNumCols();
if(type == CLEVO_KB_ITE829X)
{
if(brightness > CLEVO_829X_BRIGHTNESS_MAX)
{
brightness = CLEVO_829X_BRIGHTNESS_MAX;
}
SendCommand829x(CLEVO_829X_CMD_BRIGHTNESS, brightness, speed);
for(int row = 0; row < num_rows; row++)
{
for(int col = 0; col < num_cols; col++)
{
int idx = (row * num_cols + col) * 3;
SendCommand829x(CLEVO_829X_CMD_SET_LED,
CLEVO_829X_LED_ID(row, col),
color_data[idx + 0],
color_data[idx + 1],
color_data[idx + 2]);
}
}
return;
}
/*---------------------------------------------------------*\
| ITE 8291: row-based color transfer |
\*---------------------------------------------------------*/
unsigned char ctrl_buf[CLEVO_8291_REPORT_SIZE];
unsigned char row_buf[CLEVO_8291_ROW_DATA_SIZE];
if(brightness > CLEVO_8291_BRIGHTNESS_MAX)
{
brightness = CLEVO_8291_BRIGHTNESS_MAX;
}
memset(ctrl_buf, 0x00, CLEVO_8291_REPORT_SIZE);
ctrl_buf[0] = CLEVO_8291_CMD_MODE;
ctrl_buf[1] = CLEVO_8291_POWER_ON;
ctrl_buf[2] = CLEVO_KEYBOARD_MODE_DIRECT;
memset(ctrl_buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
ctrl_buf[0] = 0x08;
ctrl_buf[1] = 0x02; // Power on
ctrl_buf[2] = CLEVO_KEYBOARD_MODE_DIRECT; // Per-key mode
ctrl_buf[3] = 0x00; // Speed (unused)
ctrl_buf[4] = brightness;
WriteControl8291(ctrl_buf);
ctrl_buf[5] = 0x00;
ctrl_buf[6] = 0x00;
for(int row = 0; row < num_rows; row++)
WriteControl(ctrl_buf);
/*---------------------------------------------------------*\
| Send each row |
\*---------------------------------------------------------*/
for(int row = 0; row < CLEVO_KEYBOARD_NUM_ROWS; row++)
{
memset(ctrl_buf, 0x00, CLEVO_8291_REPORT_SIZE);
ctrl_buf[0] = CLEVO_8291_CMD_ROW;
/*-----------------------------------------------------*\
| Announce row data |
\*-----------------------------------------------------*/
memset(ctrl_buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE);
ctrl_buf[0] = 0x16;
ctrl_buf[1] = 0x00;
ctrl_buf[2] = row;
WriteControl8291(ctrl_buf);
memset(row_buf, 0x00, CLEVO_8291_ROW_DATA_SIZE);
WriteControl(ctrl_buf);
for(int col = 0; col < num_cols; col++)
/*-----------------------------------------------------*\
| Build row data buffer |
| Format: [pad][pad][B0..B20][G0..G20][R0..R20] |
\*-----------------------------------------------------*/
memset(row_buf, 0x00, CLEVO_KEYBOARD_ROW_DATA_SIZE);
for(int col = 0; col < CLEVO_KEYBOARD_NUM_COLS; col++)
{
int idx = (row * num_cols + col) * 3;
int led_idx = (row * CLEVO_KEYBOARD_NUM_COLS) + col;
int color_offset = led_idx * 3;
row_buf[2 + col] = color_data[idx + 2];
row_buf[2 + num_cols + col] = color_data[idx + 1];
row_buf[2 + num_cols * 2 + col] = color_data[idx + 0];
unsigned char red = color_data[color_offset + 0];
unsigned char green = color_data[color_offset + 1];
unsigned char blue = color_data[color_offset + 2];
/*-------------------------------------------------*\
| Row data layout (after 2-byte padding): |
| Bytes 2-22: Blue values for columns 0-20 |
| Bytes 23-43: Green values for columns 0-20 |
| Bytes 44-64: Red values for columns 0-20 |
\*-------------------------------------------------*/
row_buf[2 + col] = blue;
row_buf[2 + CLEVO_KEYBOARD_NUM_COLS + col] = green;
row_buf[2 + CLEVO_KEYBOARD_NUM_COLS*2 + col] = red;
}
WriteRowData(row_buf);
@@ -1,11 +1,9 @@
/*---------------------------------------------------------*\
| ClevoKeyboardController.h |
| |
| Driver for Clevo per-key RGB keyboard |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| Driver for Clevo laptop per-key RGB keyboard (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 16 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -14,75 +12,27 @@
#pragma once
#include <string>
#include <vector>
#include <hidapi.h>
#include "RGBController.h"
/*---------------------------------------------------------*\
| Device type enumeration |
\*---------------------------------------------------------*/
enum clevo_keyboard_type
{
CLEVO_KB_ITE8291,
CLEVO_KB_ITE829X,
};
/*-----------------------------------------------------*\
| ITE 8291 keyboard defines |
\*-----------------------------------------------------*/
#define CLEVO_KEYBOARD_REPORT_SIZE 8
#define CLEVO_KEYBOARD_ROW_DATA_SIZE 65
/*---------------------------------------------------------*\
| ITE 8291 keyboard defines (PID 0x600B) |
| 8 bytes, no report ID, row-based color data |
\*---------------------------------------------------------*/
#define CLEVO_8291_REPORT_SIZE 8
#define CLEVO_8291_ROW_DATA_SIZE 65
#define CLEVO_8291_NUM_ROWS 6
#define CLEVO_8291_NUM_COLS 21
#define CLEVO_8291_NUM_LEDS (CLEVO_8291_NUM_ROWS * CLEVO_8291_NUM_COLS)
#define CLEVO_8291_BRIGHTNESS_MAX 0x32
#define CLEVO_8291_CMD_MODE 0x08
#define CLEVO_8291_CMD_COLOR 0x14
#define CLEVO_8291_CMD_ROW 0x16
#define CLEVO_8291_POWER_OFF 0x01
#define CLEVO_8291_POWER_ON 0x02
#define CLEVO_8291_MODE_FLAG 0x08
#define CLEVO_KEYBOARD_NUM_ROWS 6
#define CLEVO_KEYBOARD_NUM_COLS 21
#define CLEVO_KEYBOARD_NUM_LEDS (CLEVO_KEYBOARD_NUM_ROWS * CLEVO_KEYBOARD_NUM_COLS)
/*---------------------------------------------------------*\
| ITE 829x keyboard defines (PID 0x8910) |
| 6 bytes with report ID 0xCC, per-key color commands |
| Protocol: [0xCC, cmd, d0, d1, d2, d3] |
| LED ID: ((row & 0x07) << 5) | (col & 0x1F) |
\*---------------------------------------------------------*/
#define CLEVO_829X_REPORT_ID 0xCC
#define CLEVO_829X_REPORT_SIZE 6
#define CLEVO_829X_NUM_ROWS 6
#define CLEVO_829X_NUM_COLS 20
#define CLEVO_829X_NUM_LEDS (CLEVO_829X_NUM_ROWS * CLEVO_829X_NUM_COLS)
#define CLEVO_829X_BRIGHTNESS_MAX 0x0A
#define CLEVO_829X_LED_ID(row, col) (unsigned char)((((row) & 0x07) << 5) | ((col) & 0x1F))
#define CLEVO_829X_CMD_SET_LED 0x01
#define CLEVO_829X_CMD_BRIGHTNESS 0x09
#define CLEVO_829X_CMD_ANIM 0x00
#define CLEVO_829X_CMD_CLEAR 0x0C
#define CLEVO_829X_CMD_WAVE_COLOR 0x15
#define CLEVO_829X_CMD_SNAKE_COLOR 0x16
#define CLEVO_829X_CMD_SCAN_COLOR 0x17
#define CLEVO_829X_CMD_RANDOM_COLOR 0x18
#define CLEVO_829X_COLOR_CUSTOM 0xAA
#define CLEVO_829X_COLOR_SLOT_BASE 0xA1
#define CLEVO_829X_PRESET_SLOT_BASE 0x71
#define CLEVO_829X_CMD_SAVE_BIOS 0x20
#define CLEVO_829X_CMD_STOP_ANIM 0x03
/*---------------------------------------------------------*\
| Shared defines |
\*---------------------------------------------------------*/
#define CLEVO_KEYBOARD_BRIGHTNESS_MIN 0x00
#define CLEVO_KEYBOARD_SPEED_MIN 0x00
#define CLEVO_KEYBOARD_SPEED_MAX 0x0A
#define CLEVO_KEYBOARD_SPEED_DEFAULT 0x00
#define CLEVO_KEYBOARD_BRIGHTNESS_MAX 0x32
/*---------------------------------------------------------*\
| ITE 8291 modes |
\*---------------------------------------------------------*/
#define CLEVO_KEYBOARD_SPEED_MIN 0x01
#define CLEVO_KEYBOARD_SPEED_MAX 0x0A
/*-----------------------------------------------------*\
| ITE 8291 modes |
\*-----------------------------------------------------*/
enum
{
CLEVO_KEYBOARD_MODE_DIRECT = 0x33,
@@ -97,36 +47,9 @@ enum
CLEVO_KEYBOARD_MODE_SPARK = 0x11,
};
/*---------------------------------------------------------*\
| ITE 829x firmware modes |
| Two command styles: |
| cmd=0x00, d0=mode_id (animation select) |
| cmd=mode_id, d0=0x00 (standalone effects) |
\*---------------------------------------------------------*/
enum
{
CLEVO_829X_MODE_DIRECT = 0xFD,
CLEVO_829X_MODE_WAVE = 0xA0,
CLEVO_829X_MODE_WAVE_COLOR = 0xB0,
CLEVO_829X_MODE_BREATH = 0xA1,
CLEVO_829X_MODE_BREATH_COLOR = 0xB1,
CLEVO_829X_MODE_BLINK = 0xA2,
CLEVO_829X_MODE_BLINK_COLOR = 0xB2,
CLEVO_829X_MODE_RANDOM = 0xA3,
CLEVO_829X_MODE_RANDOM_COLOR = 0xA9,
CLEVO_829X_MODE_SCAN = 0xA4,
/* Ripple (0x07) requires Insyde BIOS EC config, not supported */
CLEVO_829X_MODE_SNAKE = 0xA6,
CLEVO_829X_MODE_SNAKE_COLOR = 0xB6,
CLEVO_829X_MODE_RAINBOW_CYCLE = 0xA7,
CLEVO_829X_MODE_RAINBOW_GRAD = 0xA8,
CLEVO_829X_MODE_FN_COLOR = 0xAF,
CLEVO_829X_MODE_OFF = 0xFF,
};
/*---------------------------------------------------------*\
| Wave/reactive behaviour |
\*---------------------------------------------------------*/
/*-----------------------------------------------------*\
| Wave/reactive behaviour |
\*-----------------------------------------------------*/
enum
{
CLEVO_KEYBOARD_DIRECTION_LEFT = 0x01,
@@ -144,40 +67,23 @@ enum
class ClevoKeyboardController
{
public:
ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info, clevo_keyboard_type kb_type);
ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info);
~ClevoKeyboardController();
clevo_keyboard_type GetType();
int GetNumRows();
int GetNumCols();
int GetNumLEDs();
unsigned char GetBrightnessMax();
std::string GetDeviceLocation();
std::string GetSerialString();
std::string GetFirmwareVersion();
void TurnOff();
void SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char behaviour);
void SetModeColor(unsigned char color_idx, RGBColor color);
void SendColors(const unsigned char* color_data, unsigned char brightness, unsigned char speed);
void SetEffect829x(unsigned char mode_value, const std::vector<RGBColor>& colors, unsigned int direction = 0, unsigned int color_mode = 0);
void SetBrightnessSpeed829x(unsigned char brightness, unsigned char speed);
void SaveToBios829x();
void ClearBiosSave829x();
void StopAnimation829x();
void SetModeColor(unsigned char color_idx, unsigned char red, unsigned char green, unsigned char blue);
void SendColors(unsigned char* color_data, unsigned char brightness);
private:
hid_device* dev;
clevo_keyboard_type type;
std::string location;
unsigned short version;
unsigned char current_mode_829x = 0xFF;
unsigned char current_brightness_829x = 0xFF;
unsigned char current_speed_829x = 0xFF;
hid_device* dev;
std::string location;
unsigned short version;
void WriteControl8291(unsigned char* data);
void WriteControl(unsigned char* data);
void WriteRowData(unsigned char* data);
void SendCommand829x(unsigned char cmd, unsigned char d0 = 0, unsigned char d1 = 0, unsigned char d2 = 0, unsigned char d3 = 0);
void SendColorSlot829x(unsigned char cmd, unsigned char slot, RGBColor color);
};
@@ -1,11 +1,9 @@
/*---------------------------------------------------------*\
| ClevoKeyboardControllerDetect.cpp |
| |
| Detector for Clevo per-key RGB keyboard |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| Detector for Clevo per-key RGB keyboard (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 16 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -24,12 +22,11 @@
/*---------------------------------------------------------*\
| Clevo Keyboard product IDs |
| ITE 8291 and ITE 829x per-key RGB keyboard controllers |
| These are ITE 8291 per-key RGB keyboard controllers |
\*---------------------------------------------------------*/
#define CLEVO_KEYBOARD_PID_8291 0x600B
#define CLEVO_KEYBOARD_PID_829X 0x8910
#define CLEVO_KEYBOARD_PID_600B 0x600B
static DetectedControllers DetectClevoKeyboard(hid_device_info* info, clevo_keyboard_type kb_type)
DetectedControllers DetectClevoKeyboardControllers(hid_device_info* info, const std::string& /*name*/)
{
DetectedControllers detected_controllers;
hid_device* dev;
@@ -38,7 +35,7 @@ static DetectedControllers DetectClevoKeyboard(hid_device_info* info, clevo_keyb
if(dev)
{
ClevoKeyboardController* controller = new ClevoKeyboardController(dev, *info, kb_type);
ClevoKeyboardController* controller = new ClevoKeyboardController(dev, *info);
RGBController_ClevoKeyboard* rgb_controller = new RGBController_ClevoKeyboard(controller);
detected_controllers.push_back(rgb_controller);
@@ -47,15 +44,4 @@ static DetectedControllers DetectClevoKeyboard(hid_device_info* info, clevo_keyb
return(detected_controllers);
}
DetectedControllers DetectClevoKeyboard8291(hid_device_info* info, const std::string& /*name*/)
{
return(DetectClevoKeyboard(info, CLEVO_KB_ITE8291));
}
DetectedControllers DetectClevoKeyboard829x(hid_device_info* info, const std::string& /*name*/)
{
return(DetectClevoKeyboard(info, CLEVO_KB_ITE829X));
}
REGISTER_HID_DETECTOR_PU("Clevo Keyboard (ITE 8291)", DetectClevoKeyboard8291, ITE_VID, CLEVO_KEYBOARD_PID_8291, 0xFF03, 0x01);
REGISTER_HID_DETECTOR_PU("Clevo Keyboard (ITE 829x)", DetectClevoKeyboard829x, ITE_VID, CLEVO_KEYBOARD_PID_829X, 0xFF89, 0xCC);
REGISTER_HID_DETECTOR_PU("CLEVO Keyboard", DetectClevoKeyboardControllers, 0x048D, 0x600B, 0xFF03, 0x01);
@@ -1,11 +1,9 @@
/*---------------------------------------------------------*\
| ClevoKeyboardDevices.cpp |
| |
| Device list for Clevo per-key RGB keyboards |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| Device list for Clevo per-key RGB keyboards (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 21 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -18,79 +16,71 @@
| |
| Based on KEYBOARD_SIZE_TKL with numpad added and |
| navigation cluster adjusted to match Clevo's layout. |
| |
| Hardware LED indices (value field): |
| - Row 5 (F-keys): 105-124 |
| - Row 4 (numbers): 84-102 |
| - Row 3 (QWERTY): 63-81 |
| - Row 2 (home): 42-59 |
| - Row 1 (Z row): 22-39 |
| - Row 0 (modifiers): 0-18 |
\*---------------------------------------------------------*/
/*---------------------------------------------------------*\
| LED values, in physical (row, then column) order. |
| |
| This must match the order KeyboardLayoutManager assigns |
| default_values against: the structural keymap sorted by |
| (row, col) across main+fn_row+extras combined, NOT the |
| fn_row-then-main-then-extras order the zone tables below |
| are declared in. Getting this wrong silently misassigns |
| almost every key from Print Screen onward, not just a few.|
| LED values in TKL order (fn_row + main + extras) |
| |
| Values follow the key order in KeyboardLayoutManager.cpp. |
| For ANSI-only keys (not present on this ISO keyboard), |
| use 0 as a placeholder - they won't be displayed. |
| Numpad values are added via edit_keys. |
\*---------------------------------------------------------*/
static const std::vector<unsigned int> clevo_tkl_values =
{
/*-----------------------------------------------------*\
| Row 0: fn_row + Print Screen/Scroll Lock/Pause |
\*-----------------------------------------------------*/
/*---------------------------------------------------------*\
| Function row (keyboard_zone_fn_row) |
\*---------------------------------------------------------*/
105, // Escape
106, 107, 108, 109, // F1-F4
110, 111, 112, 113, // F5-F8
114, 115, 116, 117, // F9-F12
118, // Print Screen
0, // Scroll Lock (removed via edit_keys)
0, // Pause (removed via edit_keys)
/*-----------------------------------------------------*\
| Row 1: numbers row + Insert/Home/Page Up |
\*-----------------------------------------------------*/
/*---------------------------------------------------------*\
| Main block - Row 1 (keyboard_zone_main) |
\*---------------------------------------------------------*/
84, // Back tick
85, 86, 87, 88, 89, 90, 91, 92, 93, 94, // 1-0
95, 96, // Minus, Equals
98, // Backspace
119, // Insert
121, // Home
122, // Page Up
/*-----------------------------------------------------*\
| Row 2: Tab/QWERTY row + Delete/End/PageDown |
\*-----------------------------------------------------*/
/*---------------------------------------------------------*\
| Main block - Row 2 |
\*---------------------------------------------------------*/
63, // Tab
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, // Q-P
75, 76, // [ ]
0, // ANSI backslash (not on ISO)
120, // Delete
124, // End
123, // Page Down
/*-----------------------------------------------------*\
| Row 3: Caps Lock/home row/Enter |
\*-----------------------------------------------------*/
/*---------------------------------------------------------*\
| Main block - Row 3 |
\*---------------------------------------------------------*/
42, // Caps Lock
44, 45, 46, 47, 48, 49, 50, 51, 52, // A-L
53, 54, // ; '
55, // ISO # (POUND)
56, // Enter
77, // Enter (ANSI/ISO share same LED)
/*-----------------------------------------------------*\
| Row 4: Left Shift/ISO backslash/Z row + Up |
\*-----------------------------------------------------*/
21, // Left Shift
77, // ISO backslash
/*---------------------------------------------------------*\
| Main block - Row 4 |
\*---------------------------------------------------------*/
22, // Left Shift
23, // ISO backslash
24, 25, 26, 27, 28, 29, 30, 31, 32, // Z-. (9 keys)
33, // /
35, // Right Shift
14, // Up
/*-----------------------------------------------------*\
| Row 5: bottom modifier row + Left/Down/Right |
\*-----------------------------------------------------*/
/*---------------------------------------------------------*\
| Main block - Row 5 |
\*---------------------------------------------------------*/
0, // Left Ctrl
3, // Left Win
4, // Left Alt
@@ -99,53 +89,29 @@ static const std::vector<unsigned int> clevo_tkl_values =
0, // Right Fn (removed via edit_keys)
0, // Menu (removed via edit_keys)
12, // Right Ctrl
/*---------------------------------------------------------*\
| Extras - Navigation cluster (keyboard_zone_extras) |
\*---------------------------------------------------------*/
118, // Print Screen
0, // Scroll Lock (removed via edit_keys)
0, // Pause (removed via edit_keys)
119, // Insert
121, // Home
123, // Page Up
120, // Delete
122, // End
124, // Page Down
/*---------------------------------------------------------*\
| Extras - Arrow keys |
\*---------------------------------------------------------*/
14, // Up
13, // Left
18, // Down
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,
@@ -156,9 +122,10 @@ keyboard_keymap_overlay_values clevo_keyboard_layout
}
},
{
/*---------------------------------------------------------*\
| Edit Keys |
\*---------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------*\
| Edit Keys |
| Zone, Row, Column, Value, Key, Alternate Name, OpCode, |
\*-------------------------------------------------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------*\
| Remove keys not present on Clevo keyboard |
@@ -1,11 +1,9 @@
/*---------------------------------------------------------*\
| ClevoKeyboardDevices.h |
| |
| Device list for Clevo per-key RGB keyboards |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| Device list for Clevo per-key RGB keyboards (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 21 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -16,8 +14,7 @@
#include <string>
#include "KeyboardLayoutManager.h"
/*---------------------------------------------------------*\
| Clevo keyboard layout definitions |
\*---------------------------------------------------------*/
/*-----------------------------------------------------*\
| Clevo keyboard layout definitions |
\*-----------------------------------------------------*/
extern keyboard_keymap_overlay_values clevo_keyboard_layout;
extern keyboard_keymap_overlay_values clevo_829x_keyboard_layout;
@@ -1,17 +1,14 @@
/*---------------------------------------------------------*\
| RGBController_ClevoKeyboard.cpp |
| |
| RGBController for Clevo per-key RGB keyboard |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| RGBController for Clevo per-key RGB keyboard (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 16 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <vector>
#include "RGBController_ClevoKeyboard.h"
#include "KeyboardLayoutManager.h"
@@ -43,306 +40,152 @@ RGBController_ClevoKeyboard::RGBController_ClevoKeyboard(ClevoKeyboardController
Direct.value = CLEVO_KEYBOARD_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Direct.brightness_max = controller->GetBrightnessMax();
Direct.brightness = controller->GetBrightnessMax();
Direct.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Direct.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
/*---------------------------------------------------------*\
| ITE 8291 built-in effects (not available on ITE 829x) |
\*---------------------------------------------------------*/
if(controller->GetType() == CLEVO_KB_ITE8291)
{
mode Rainbow;
Rainbow.name = "Rainbow";
Rainbow.value = CLEVO_KEYBOARD_MODE_RAINBOW;
Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
Rainbow.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Rainbow.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Rainbow.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Rainbow.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Rainbow.brightness_max = controller->GetBrightnessMax();
Rainbow.brightness = controller->GetBrightnessMax();
Rainbow.color_mode = MODE_COLORS_NONE;
modes.push_back(Rainbow);
mode Rainbow;
Rainbow.name = "Rainbow";
Rainbow.value = CLEVO_KEYBOARD_MODE_RAINBOW;
Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
Rainbow.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Rainbow.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Rainbow.speed = 0x05;
Rainbow.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Rainbow.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Rainbow.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Rainbow.color_mode = MODE_COLORS_NONE;
modes.push_back(Rainbow);
mode Wave;
Wave.name = "Wave";
Wave.value = CLEVO_KEYBOARD_MODE_WAVE;
Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Wave.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Wave.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Wave.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Wave.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Wave.brightness_max = controller->GetBrightnessMax();
Wave.brightness = controller->GetBrightnessMax();
Wave.direction = MODE_DIRECTION_LEFT;
Wave.colors_min = 1;
Wave.colors_max = 1;
Wave.colors.resize(1);
Wave.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Wave);
mode Wave;
Wave.name = "Wave";
Wave.value = CLEVO_KEYBOARD_MODE_WAVE;
Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Wave.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Wave.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Wave.speed = 0x05;
Wave.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Wave.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Wave.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Wave.direction = MODE_DIRECTION_LEFT;
Wave.colors_min = 1;
Wave.colors_max = 1;
Wave.colors.resize(1);
Wave.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Wave);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = CLEVO_KEYBOARD_MODE_BREATH;
Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Breathing.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Breathing.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Breathing.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Breathing.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Breathing.brightness_max = controller->GetBrightnessMax();
Breathing.brightness = controller->GetBrightnessMax();
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.colors.resize(1);
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Breathing);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = CLEVO_KEYBOARD_MODE_BREATH;
Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Breathing.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Breathing.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Breathing.speed = 0x05;
Breathing.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Breathing.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Breathing.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.colors.resize(1);
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Breathing);
mode Reactive;
Reactive.name = "Reactive";
Reactive.value = CLEVO_KEYBOARD_MODE_REACTIVE;
Reactive.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Reactive.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Reactive.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Reactive.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Reactive.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Reactive.brightness_max = controller->GetBrightnessMax();
Reactive.brightness = controller->GetBrightnessMax();
Reactive.colors_min = 1;
Reactive.colors_max = 1;
Reactive.colors.resize(1);
Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Reactive);
mode Reactive;
Reactive.name = "Reactive";
Reactive.value = CLEVO_KEYBOARD_MODE_REACTIVE;
Reactive.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Reactive.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Reactive.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Reactive.speed = 0x05;
Reactive.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Reactive.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Reactive.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Reactive.colors_min = 1;
Reactive.colors_max = 1;
Reactive.colors.resize(1);
Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Reactive);
mode Ripple;
Ripple.name = "Ripple";
Ripple.value = CLEVO_KEYBOARD_MODE_RIPPLE;
Ripple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Ripple.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Ripple.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Ripple.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Ripple.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Ripple.brightness_max = controller->GetBrightnessMax();
Ripple.brightness = controller->GetBrightnessMax();
Ripple.colors_min = 1;
Ripple.colors_max = 1;
Ripple.colors.resize(1);
Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Ripple);
mode Ripple;
Ripple.name = "Ripple";
Ripple.value = CLEVO_KEYBOARD_MODE_RIPPLE;
Ripple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Ripple.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Ripple.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Ripple.speed = 0x05;
Ripple.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Ripple.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Ripple.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Ripple.colors_min = 1;
Ripple.colors_max = 1;
Ripple.colors.resize(1);
Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Ripple);
mode Marquee;
Marquee.name = "Marquee";
Marquee.value = CLEVO_KEYBOARD_MODE_MARQUEE;
Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Marquee.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Marquee.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Marquee.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Marquee.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Marquee.brightness_max = controller->GetBrightnessMax();
Marquee.brightness = controller->GetBrightnessMax();
Marquee.colors_min = 1;
Marquee.colors_max = 1;
Marquee.colors.resize(1);
Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Marquee);
mode Marquee;
Marquee.name = "Marquee";
Marquee.value = CLEVO_KEYBOARD_MODE_MARQUEE;
Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Marquee.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Marquee.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Marquee.speed = 0x05;
Marquee.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Marquee.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Marquee.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Marquee.colors_min = 1;
Marquee.colors_max = 1;
Marquee.colors.resize(1);
Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Marquee);
mode Raindrop;
Raindrop.name = "Raindrop";
Raindrop.value = CLEVO_KEYBOARD_MODE_RAINDROP;
Raindrop.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Raindrop.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Raindrop.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Raindrop.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Raindrop.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Raindrop.brightness_max = controller->GetBrightnessMax();
Raindrop.brightness = controller->GetBrightnessMax();
Raindrop.colors_min = 1;
Raindrop.colors_max = 1;
Raindrop.colors.resize(1);
Raindrop.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Raindrop);
mode Raindrop;
Raindrop.name = "Raindrop";
Raindrop.value = CLEVO_KEYBOARD_MODE_RAINDROP;
Raindrop.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Raindrop.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Raindrop.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Raindrop.speed = 0x05;
Raindrop.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Raindrop.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Raindrop.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Raindrop.colors_min = 1;
Raindrop.colors_max = 1;
Raindrop.colors.resize(1);
Raindrop.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Raindrop);
mode Aurora;
Aurora.name = "Aurora";
Aurora.value = CLEVO_KEYBOARD_MODE_AURORA;
Aurora.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Aurora.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Aurora.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Aurora.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Aurora.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Aurora.brightness_max = controller->GetBrightnessMax();
Aurora.brightness = controller->GetBrightnessMax();
Aurora.colors_min = 1;
Aurora.colors_max = 1;
Aurora.colors.resize(1);
Aurora.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Aurora);
mode Aurora;
Aurora.name = "Aurora";
Aurora.value = CLEVO_KEYBOARD_MODE_AURORA;
Aurora.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Aurora.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Aurora.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Aurora.speed = 0x05;
Aurora.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Aurora.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Aurora.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Aurora.colors_min = 1;
Aurora.colors_max = 1;
Aurora.colors.resize(1);
Aurora.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Aurora);
mode Spark;
Spark.name = "Spark";
Spark.value = CLEVO_KEYBOARD_MODE_SPARK;
Spark.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Spark.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Spark.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Spark.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Spark.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Spark.brightness_max = controller->GetBrightnessMax();
Spark.brightness = controller->GetBrightnessMax();
Spark.colors_min = 1;
Spark.colors_max = 1;
Spark.colors.resize(1);
Spark.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Spark);
}
/*---------------------------------------------------------*\
| ITE 829x firmware effects |
\*---------------------------------------------------------*/
if(controller->GetType() == CLEVO_KB_ITE829X)
{
unsigned char bmax = controller->GetBrightnessMax();
mode Wave;
Wave.name = "Wave";
Wave.value = CLEVO_829X_MODE_WAVE;
Wave.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_DIRECTION_DIAG;
Wave.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Wave.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Wave.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Wave.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Wave.brightness_max = bmax;
Wave.brightness = bmax;
Wave.direction = MODE_DIRECTION_LEFT;
Wave.colors_min = 1;
Wave.colors_max = 1;
Wave.colors.resize(1, ToRGBColor(255, 0, 0));
Wave.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Wave);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = CLEVO_829X_MODE_BREATH;
Breathing.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Breathing.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Breathing.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Breathing.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Breathing.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Breathing.brightness_max = bmax;
Breathing.brightness = bmax;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.colors.resize(1, ToRGBColor(255, 0, 0));
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Breathing);
mode Flashing;
Flashing.name = "Flashing";
Flashing.value = CLEVO_829X_MODE_BLINK;
Flashing.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Flashing.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Flashing.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Flashing.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Flashing.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Flashing.brightness_max = bmax;
Flashing.brightness = bmax;
Flashing.colors_min = 1;
Flashing.colors_max = 1;
Flashing.colors.resize(1, ToRGBColor(255, 0, 0));
Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Flashing);
mode Random;
Random.name = "Random";
Random.value = CLEVO_829X_MODE_RANDOM;
Random.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Random.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Random.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Random.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Random.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Random.brightness_max = bmax;
Random.brightness = bmax;
Random.colors_min = 1;
Random.colors_max = 1;
Random.colors.resize(1, ToRGBColor(255, 0, 0));
Random.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Random);
mode Scan;
Scan.name = "Scan";
Scan.value = CLEVO_829X_MODE_SCAN;
Scan.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Scan.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Scan.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Scan.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Scan.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Scan.brightness_max = bmax;
Scan.brightness = bmax;
Scan.colors_min = 2;
Scan.colors_max = 2;
Scan.colors.resize(2);
Scan.colors[0] = ToRGBColor(255, 0, 0);
Scan.colors[1] = ToRGBColor(0, 0, 255);
Scan.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Scan);
mode Snake;
Snake.name = "Snake";
Snake.value = CLEVO_829X_MODE_SNAKE;
Snake.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_DIAG;
Snake.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
Snake.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
Snake.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
Snake.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Snake.brightness_max = bmax;
Snake.brightness = bmax;
Snake.direction = MODE_DIRECTION_UP_LEFT;
Snake.colors_min = 1;
Snake.colors_max = 1;
Snake.colors.resize(1, ToRGBColor(255, 0, 0));
Snake.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Snake);
mode RainbowCycle;
RainbowCycle.name = "Spectrum Cycle";
RainbowCycle.value = CLEVO_829X_MODE_RAINBOW_CYCLE;
RainbowCycle.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
RainbowCycle.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
RainbowCycle.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
RainbowCycle.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
RainbowCycle.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
RainbowCycle.brightness_max = bmax;
RainbowCycle.brightness = bmax;
RainbowCycle.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowCycle);
mode RainbowGrad;
RainbowGrad.name = "Rainbow Wave";
RainbowGrad.value = CLEVO_829X_MODE_RAINBOW_GRAD;
RainbowGrad.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_DIRECTION_DIAG;
RainbowGrad.speed_min = CLEVO_KEYBOARD_SPEED_MIN;
RainbowGrad.speed_max = CLEVO_KEYBOARD_SPEED_MAX;
RainbowGrad.speed = CLEVO_KEYBOARD_SPEED_DEFAULT;
RainbowGrad.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
RainbowGrad.brightness_max = bmax;
RainbowGrad.brightness = bmax;
RainbowGrad.direction = MODE_DIRECTION_LEFT;
RainbowGrad.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowGrad);
mode FnColor;
FnColor.name = "Fn Highlight";
FnColor.value = CLEVO_829X_MODE_FN_COLOR;
FnColor.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
FnColor.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
FnColor.brightness_max = bmax;
FnColor.brightness = bmax;
FnColor.colors_min = 1;
FnColor.colors_max = 1;
FnColor.colors.resize(1, ToRGBColor(255, 0, 0));
FnColor.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(FnColor);
}
mode Spark;
Spark.name = "Spark";
Spark.value = CLEVO_KEYBOARD_MODE_SPARK;
Spark.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Spark.speed_min = CLEVO_KEYBOARD_SPEED_MAX;
Spark.speed_max = CLEVO_KEYBOARD_SPEED_MIN;
Spark.speed = 0x05;
Spark.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN;
Spark.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Spark.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX;
Spark.colors_min = 1;
Spark.colors_max = 1;
Spark.colors.resize(1);
Spark.color_mode = MODE_COLORS_MODE_SPECIFIC;
modes.push_back(Spark);
mode Off;
Off.name = "Off";
@@ -364,22 +207,16 @@ RGBController_ClevoKeyboard::~RGBController_ClevoKeyboard()
void RGBController_ClevoKeyboard::SetupZones()
{
/*---------------------------------------------------------*\
| Select layout based on device type |
| Create keyboard layout using KeyboardLayoutManager |
\*---------------------------------------------------------*/
bool is_829x = (controller->GetType() == CLEVO_KB_ITE829X);
keyboard_keymap_overlay_values& layout = is_829x
? clevo_829x_keyboard_layout
: clevo_keyboard_layout;
KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ISO_QWERTY,
layout.base_size,
layout.key_values);
clevo_keyboard_layout.base_size,
clevo_keyboard_layout.key_values);
new_kb.ChangeKeys(layout);
new_kb.ChangeKeys(clevo_keyboard_layout);
/*---------------------------------------------------------*\
| Create keyboard zone from KLM |
| Create a matrix zone for the keyboard |
\*---------------------------------------------------------*/
zone keyboard_zone;
@@ -396,6 +233,9 @@ void RGBController_ClevoKeyboard::SetupZones()
zones.push_back(keyboard_zone);
/*---------------------------------------------------------*\
| Create LEDs from the KeyboardLayoutManager data |
\*---------------------------------------------------------*/
for(unsigned int led_idx = 0; led_idx < keyboard_zone.leds_count; led_idx++)
{
led new_led;
@@ -409,48 +249,36 @@ void RGBController_ClevoKeyboard::SetupZones()
SetupColors();
/*---------------------------------------------------------*\
| Build buffer map from LED values to color pointers |
| 829x LED IDs have gaps (stride 32), need larger map |
| Create buffer map to translate OpenRGB LED order to |
| hardware LED order. The hardware expects 126 color values |
| indexed by LED position (0-125). |
\*---------------------------------------------------------*/
null_color = 0x00000000;
size_t map_size = is_829x
? CLEVO_829X_LED_ID(CLEVO_829X_NUM_ROWS - 1, CLEVO_829X_NUM_COLS - 1) + 1
: (size_t)controller->GetNumLEDs();
buffer_map.resize(map_size, &null_color);
buffer_map.resize(CLEVO_KEYBOARD_NUM_LEDS, &null_color);
for(size_t led_idx = 0; led_idx < leds.size(); led_idx++)
{
if(leds[led_idx].value < buffer_map.size())
{
buffer_map[leds[led_idx].value] = &colors[led_idx];
}
buffer_map[leds[led_idx].value] = &colors[led_idx];
}
}
void RGBController_ClevoKeyboard::DeviceUpdateLEDs()
{
int num_rows = controller->GetNumRows();
int num_cols = controller->GetNumCols();
bool is_829x = (controller->GetType() == CLEVO_KB_ITE829X);
/*---------------------------------------------------------*\
| Build color data array using buffer map and send to device|
| The buffer_map translates from hardware LED index to the |
| corresponding color pointer for that LED. |
\*---------------------------------------------------------*/
unsigned char color_data[CLEVO_KEYBOARD_NUM_LEDS * 3];
std::vector<unsigned char> color_data(num_rows * num_cols * 3);
for(int row = 0; row < num_rows; row++)
for(int i = 0; i < CLEVO_KEYBOARD_NUM_LEDS; i++)
{
for(int col = 0; col < num_cols; col++)
{
int seq = row * num_cols + col;
int led_id = is_829x ? CLEVO_829X_LED_ID(row, col) : seq;
color_data[seq * 3 + 0] = RGBGetRValue(*buffer_map[led_id]);
color_data[seq * 3 + 1] = RGBGetGValue(*buffer_map[led_id]);
color_data[seq * 3 + 2] = RGBGetBValue(*buffer_map[led_id]);
}
color_data[i * 3 + 0] = RGBGetRValue(*buffer_map[i]);
color_data[i * 3 + 1] = RGBGetGValue(*buffer_map[i]);
color_data[i * 3 + 2] = RGBGetBValue(*buffer_map[i]);
}
controller->SendColors(color_data.data(), modes[active_mode].brightness, modes[active_mode].speed);
controller->SendColors(color_data, modes[active_mode].brightness);
}
void RGBController_ClevoKeyboard::DeviceUpdateZoneLEDs(int /*zone*/)
@@ -476,16 +304,6 @@ void RGBController_ClevoKeyboard::DeviceUpdateMode()
return;
}
/*---------------------------------------------------------*\
| Handle ITE 829x firmware effects |
\*---------------------------------------------------------*/
if(controller->GetType() == CLEVO_KB_ITE829X && mode_value != CLEVO_KEYBOARD_MODE_DIRECT)
{
controller->SetEffect829x(mode_value, modes[active_mode].colors, modes[active_mode].direction, modes[active_mode].color_mode);
controller->SetBrightnessSpeed829x(modes[active_mode].brightness, modes[active_mode].speed);
return;
}
/*---------------------------------------------------------*\
| Handle Direct (per-key) mode |
\*---------------------------------------------------------*/
@@ -500,26 +318,41 @@ void RGBController_ClevoKeyboard::DeviceUpdateMode()
\*---------------------------------------------------------*/
unsigned char brightness = modes[active_mode].brightness;
unsigned char speed = modes[active_mode].speed;
unsigned char behaviour = (mode_value == CLEVO_KEYBOARD_MODE_WAVE)
? modes[active_mode].direction + 1
: 0x00;
unsigned char behaviour = 0x00;
/*---------------------------------------------------------*\
| Set direction for wave mode |
\*---------------------------------------------------------*/
if(mode_value == CLEVO_KEYBOARD_MODE_WAVE)
{
switch(modes[active_mode].direction)
{
case MODE_DIRECTION_LEFT:
behaviour = CLEVO_KEYBOARD_DIRECTION_LEFT;
break;
case MODE_DIRECTION_RIGHT:
behaviour = CLEVO_KEYBOARD_DIRECTION_RIGHT;
break;
case MODE_DIRECTION_UP:
behaviour = CLEVO_KEYBOARD_DIRECTION_UP;
break;
case MODE_DIRECTION_DOWN:
behaviour = CLEVO_KEYBOARD_DIRECTION_DOWN;
break;
}
}
/*---------------------------------------------------------*\
| Set mode color if applicable |
\*---------------------------------------------------------*/
if(modes[active_mode].colors.size() > 0)
{
controller->SetModeColor(1, modes[active_mode].colors[0]);
unsigned char red = RGBGetRValue(modes[active_mode].colors[0]);
unsigned char green = RGBGetGValue(modes[active_mode].colors[0]);
unsigned char blue = RGBGetBValue(modes[active_mode].colors[0]);
controller->SetModeColor(1, red, green, blue);
}
controller->SetMode(mode_value, brightness, speed, behaviour);
}
void RGBController_ClevoKeyboard::DeviceSaveMode()
{
if(controller->GetType() == CLEVO_KB_ITE829X)
{
DeviceUpdateMode();
controller->SaveToBios829x();
}
}
@@ -1,11 +1,9 @@
/*---------------------------------------------------------*\
| RGBController_ClevoKeyboard.h |
| |
| RGBController for Clevo per-key RGB keyboard |
| Supports ITE 8291 (PID 0x600B) and ITE 829x (0x8910) |
| RGBController for Clevo per-key RGB keyboard (ITE 8291) |
| |
| Kyle Cascade ([email protected]) 16 Jan 2026 |
| Valentin Lobstein ([email protected]) 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -20,7 +18,7 @@
class RGBController_ClevoKeyboard : public RGBController
{
public:
explicit RGBController_ClevoKeyboard(ClevoKeyboardController* controller_ptr);
RGBController_ClevoKeyboard(ClevoKeyboardController* controller_ptr);
~RGBController_ClevoKeyboard();
void SetupZones();
@@ -30,7 +28,6 @@ public:
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
ClevoKeyboardController* controller;
@@ -1,4 +1,4 @@
/*---------------------------------------------------------*\
/*---------------------------------------------------------*\
| ColorfulGPUControllerDetect.cpp |
| |
| Detector for Colorful GPU |
@@ -51,7 +51,6 @@ DetectedControllers DetectColorfulGPUControllers(i2c_smbus_interface* bus, uint8
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Advanced OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ADVANCED_OC_12G_LV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA106_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA106_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61);
@@ -72,7 +71,6 @@ REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Ti SUPER Advanced OC-V", Detec
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Ti SUPER Ultra W", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070TI_SUPER_ULTRA_W, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 SUPER Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070_SUPER_ULTRA_W_OC, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Vulcan OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070_VULCAN_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070_ULTRAW_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 SUPER Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070S_ULTRAW_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4080_ULTRAW_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4080_ULTRAW_OCV2, 0x61);
@@ -13,6 +13,11 @@
#include "CMSmallARGBController.h"
#include "StringUtils.h"
cm_small_argb_headers cm_small_argb_header_data[1] =
{
{ "CM Small ARGB", 0x01, true, 12 }
};
CMSmallARGBController::CMSmallARGBController(hid_device* dev_handle, char *_path)
{
dev = dev_handle;
@@ -87,24 +92,6 @@ std::string CMSmallARGBController::GetSerial()
return(StringUtils::wstring_to_string(serial_string));
}
std::string CMSmallARGBController::GetVersion()
{
/*-----------------------------------------------------*\
| This device uses the serial value to determine the |
| version. It does not report a proper unique serial. |
\*-----------------------------------------------------*/
std::string serial_string = GetSerial();
if(serial_string == CM_SMALL_ARGB_FW0012)
{
return("0012");
}
else
{
return("Unsupported");
}
}
std::string CMSmallARGBController::GetLocation()
{
return("HID: " + location);
@@ -135,11 +122,6 @@ unsigned char CMSmallARGBController::GetLedSpeed()
return(current_speed);
}
unsigned char CMSmallARGBController::GetBrightness()
{
return(current_brightness);
}
bool CMSmallARGBController::GetRandomColours()
{
return(bool_random);
@@ -235,9 +217,9 @@ void CMSmallARGBController::SendUpdate()
hid_write(dev, buffer, buffer_size);
buffer[CM_SMALL_ARGB_COMMAND_BYTE] = 0x0B;
buffer[CM_SMALL_ARGB_FUNCTION_BYTE] = 0x02;
buffer[CM_SMALL_ARGB_ZONE_BYTE] = 0x01;
buffer[CM_SMALL_ARGB_COMMAND_BYTE] = 0x0b;
buffer[CM_SMALL_ARGB_FUNCTION_BYTE] = (false) ? 0x01 : 0x02; //This controls custom mode TODO
buffer[CM_SMALL_ARGB_ZONE_BYTE] = cm_small_argb_header_data[0].header;
buffer[CM_SMALL_ARGB_MODE_BYTE] = current_mode;
buffer[CM_SMALL_ARGB_SPEED_BYTE] = current_speed;
buffer[CM_SMALL_ARGB_COLOUR_INDEX_BYTE] = (bool_random) ? 0x00 : 0x10; //This looks to still be the colour index and controls random colours
@@ -39,6 +39,16 @@ enum
CM_SMALL_ARGB_BLUE_BYTE = 11,
};
struct cm_small_argb_headers
{
const char* name;
unsigned char header;
bool digital;
unsigned int count;
};
extern cm_small_argb_headers cm_small_argb_header_data[1];
enum
{
CM_SMALL_ARGB_MODE_SPECTRUM = 0x01, //Spectrum Mode
@@ -61,8 +71,6 @@ enum
CM_SMALL_ARGB_SPEED_FASTEST = 0x04, // Fastest speed
};
#define CM_SMALL_ARGB_FW0012 std::string("A202104052336")
class CMSmallARGBController
{
public:
@@ -72,14 +80,12 @@ public:
std::string GetDeviceName();
std::string GetSerial();
std::string GetLocation();
std::string GetVersion();
unsigned char GetMode();
unsigned char GetLedRed();
unsigned char GetLedGreen();
unsigned char GetLedBlue();
unsigned char GetLedSpeed();
unsigned char GetBrightness();
bool GetRandomColours();
void SetLedCount(int zone, int led_count);
@@ -27,68 +27,22 @@
RGBController_CMSmallARGBController::RGBController_CMSmallARGBController(CMSmallARGBController* controller_ptr)
{
controller = controller_ptr;
unsigned char speed = controller->GetLedSpeed();
name = controller->GetDeviceName();
name = cm_small_argb_header_data[0].name;
vendor = "Cooler Master";
type = DEVICE_TYPE_LEDSTRIP;
description = "Cooler Master Small ARGB Controller Device";
version = controller->GetVersion();
description = controller->GetDeviceName();
version = "2.0 for FW0012";
serial = controller->GetSerial();
location = controller->GetLocation();
SetupModes();
SetupZones();
/*-----------------------------------------------------*\
| Initialize the active mode to the device's current |
| mode |
\*-----------------------------------------------------*/
int device_mode = controller->GetMode();
for(std::size_t mode_idx = 0; mode_idx < modes.size(); mode_idx++)
{
if(modes[mode_idx].value == device_mode)
{
active_mode = (int)mode_idx;
if((modes[mode_idx].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) && (modes[mode_idx].colors.size() > 0))
{
modes[mode_idx].colors[0] = ToRGBColor(controller->GetLedRed(), controller->GetLedGreen(), controller->GetLedBlue());
}
if(modes[mode_idx].flags & MODE_FLAG_HAS_SPEED)
{
modes[mode_idx].speed = controller->GetLedSpeed();
}
if(modes[mode_idx].flags & MODE_FLAG_HAS_BRIGHTNESS)
{
modes[mode_idx].brightness = controller->GetBrightness();
}
if(modes[mode_idx].flags & MODE_FLAG_HAS_RANDOM_COLOR)
{
modes[mode_idx].color_mode = controller->GetRandomColours() ? MODE_COLORS_RANDOM : MODE_COLORS_MODE_SPECIFIC;
}
break;
}
}
}
void RGBController_CMSmallARGBController::SetupModes()
{
/*-----------------------------------------------------*\
| The Small ARGB only supports Direct mode from FW0012 |
| onwards, older firmware does not accept direct mode |
| packets |
\*-----------------------------------------------------*/
//if(serial >= CM_SMALL_ARGB_FW0012)
if(serial >= CM_SMALL_ARGB_FW0012)
{
mode Direct;
Direct.name = "Direct";
Direct.value = CM_SMALL_ARGB_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_REQUIRES_ENTIRE_DEVICE;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.brightness_min = 0;
Direct.brightness_max = CM_SMALL_ARGB_BRIGHTNESS_MAX;
Direct.brightness = CM_SMALL_ARGB_BRIGHTNESS_MAX;
@@ -115,7 +69,7 @@ void RGBController_CMSmallARGBController::SetupModes()
Reload.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST;
Reload.speed_max = CM_SMALL_ARGB_SPEED_FASTEST;
Reload.color_mode = MODE_COLORS_RANDOM;
Reload.speed = CM_SMALL_ARGB_SPEED_NORMAL;
Reload.speed = speed;
modes.push_back(Reload);
mode Recoil;
@@ -131,7 +85,7 @@ void RGBController_CMSmallARGBController::SetupModes()
Recoil.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST;
Recoil.speed_max = CM_SMALL_ARGB_SPEED_FASTEST;
Recoil.color_mode = MODE_COLORS_RANDOM;
Recoil.speed = CM_SMALL_ARGB_SPEED_NORMAL;
Recoil.speed = speed;
modes.push_back(Recoil);
mode Breathing;
@@ -147,7 +101,7 @@ void RGBController_CMSmallARGBController::SetupModes()
Breathing.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST;
Breathing.speed_max = CM_SMALL_ARGB_SPEED_FASTEST;
Breathing.color_mode = MODE_COLORS_RANDOM;
Breathing.speed = CM_SMALL_ARGB_SPEED_NORMAL;
Breathing.speed = speed;
modes.push_back(Breathing);
mode Refill;
@@ -163,7 +117,7 @@ void RGBController_CMSmallARGBController::SetupModes()
Refill.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST;
Refill.speed_max = CM_SMALL_ARGB_SPEED_FASTEST;
Refill.color_mode = MODE_COLORS_RANDOM;
Refill.speed = CM_SMALL_ARGB_SPEED_NORMAL;
Refill.speed = speed;
modes.push_back(Refill);
mode Demo;
@@ -176,7 +130,7 @@ void RGBController_CMSmallARGBController::SetupModes()
Demo.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST;
Demo.speed_max = CM_SMALL_ARGB_SPEED_FASTEST;
Demo.color_mode = MODE_COLORS_NONE;
Demo.speed = CM_SMALL_ARGB_SPEED_NORMAL;
Demo.speed = speed;
modes.push_back(Demo);
mode Spectrum;
@@ -189,19 +143,39 @@ void RGBController_CMSmallARGBController::SetupModes()
Spectrum.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST;
Spectrum.speed_max = CM_SMALL_ARGB_SPEED_FASTEST;
Spectrum.color_mode = MODE_COLORS_NONE;
Spectrum.speed = CM_SMALL_ARGB_SPEED_NORMAL;
Spectrum.speed = speed;
modes.push_back(Spectrum);
/*-----------------------------------------------------*\
| Define Passthrough mode as requiring the entire |
| device, it hands control over to the motherboard |
\*-----------------------------------------------------*/
mode PassThru;
PassThru.name = "Pass Thru";
PassThru.value = CM_SMALL_ARGB_MODE_PASSTHRU;
PassThru.flags = MODE_FLAG_REQUIRES_ENTIRE_DEVICE;
PassThru.color_mode = MODE_COLORS_NONE;
modes.push_back(PassThru);
SetupZones();
int temp_mode = controller->GetMode();
for(int mode_idx = 0; mode_idx < (int)modes.size() ; mode_idx++)
{
if(temp_mode == modes[mode_idx].value)
{
active_mode = mode_idx;
break;
}
}
if (modes[active_mode].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR)
{
modes[active_mode].colors[0] = ToRGBColor(controller->GetLedRed(), controller->GetLedGreen(), controller->GetLedBlue());
}
modes[active_mode].color_mode = (controller->GetRandomColours()) ? MODE_COLORS_RANDOM : MODE_COLORS_MODE_SPECIFIC;
if (modes[active_mode].flags & MODE_FLAG_HAS_SPEED)
{
modes[active_mode].speed = controller->GetLedSpeed();
}
}
RGBController_CMSmallARGBController::~RGBController_CMSmallARGBController()
@@ -274,9 +248,7 @@ void RGBController_CMSmallARGBController::SetupZones()
{
led new_led;
new_led.name = zones[zone_idx].name;
new_led.name.append(", LED ");
new_led.name.append(std::to_string(lp_idx + 1));
new_led.value = (unsigned int)zone_idx;
new_led.name.append(", LED " + std::to_string(lp_idx));
leds.push_back(new_led);
}
@@ -289,7 +261,7 @@ void RGBController_CMSmallARGBController::DeviceConfigureZone(int zone_idx)
{
if((size_t)zone_idx < zones.size())
{
controller->SetLedCount(1, zones[zone_idx].leds_count);
controller->SetLedCount(cm_small_argb_header_data[zone_idx].header, zones[zone_idx].leds_count);
SetupZones();
}
@@ -305,79 +277,37 @@ void RGBController_CMSmallARGBController::DeviceUpdateLEDs()
void RGBController_CMSmallARGBController::DeviceUpdateZoneLEDs(int zone)
{
/*-----------------------------------------------------*\
| Only send direct packets while Direct mode is active. |
| Direct mode is only offered on FW0012 and newer, so |
| older firmware will never send direct packets here |
\*-----------------------------------------------------*/
if(modes[active_mode].value != CM_SMALL_ARGB_MODE_DIRECT)
if(serial >= CM_SMALL_ARGB_FW0012)
{
return;
controller->SetLedsDirect( zones[zone].colors, zones[zone].leds_count );
}
controller->SetLedsDirect(zones[zone].colors, zones[zone].leds_count);
}
void RGBController_CMSmallARGBController::DeviceUpdateSingleLED(int led)
{
/*-----------------------------------------------------*\
| Map the LED back to its zone and update that zone |
\*-----------------------------------------------------*/
unsigned int zone_idx = leds[led].value;
UpdateZoneLEDs(zone_idx);
DeviceUpdateZoneLEDs(led);
}
void RGBController_CMSmallARGBController::SetCustomMode()
{
/*-----------------------------------------------------*\
| Direct mode is only supported on FW0012 and newer |
\*-----------------------------------------------------*/
/*-------------------------------------------------*\
| The small ARGB may not support "Direct" mode |
| in which case this will select "Pass Thru" |
\*-------------------------------------------------*/
if(serial >= CM_SMALL_ARGB_FW0012)
{
/*-------------------------------------------------*\
| Search the mode list for Direct mode |
\*-------------------------------------------------*/
for(std::size_t mode_idx = 0; mode_idx < modes.size(); mode_idx++)
{
if(modes[mode_idx].name == "Direct")
{
SetActiveMode((int)mode_idx);
break;
}
}
active_mode = 0;
}
else
{
/*-------------------------------------------------*\
| Direct mode is unsupported by the firmware, |
| fall back to Pass Thru |
\*-------------------------------------------------*/
for(std::size_t mode_idx = 0; mode_idx < modes.size(); mode_idx++)
{
if(modes[mode_idx].value == CM_SMALL_ARGB_MODE_PASSTHRU)
{
SetActiveMode((int)mode_idx);
break;
}
}
active_mode = 7;
}
}
void RGBController_CMSmallARGBController::DeviceUpdateMode()
{
/*-----------------------------------------------------*\
| Set up mode parameters |
\*-----------------------------------------------------*/
bool random_colours = (modes[active_mode].color_mode == MODE_COLORS_RANDOM);
RGBColor colour = 0;
unsigned int speed = modes[active_mode].speed;
unsigned int brightness = modes[active_mode].brightness;
bool random_colours = (modes[active_mode].color_mode == MODE_COLORS_RANDOM);
RGBColor colour = (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) ? modes[active_mode].colors[0] : 0;
if(modes[active_mode].colors.size() > 0)
{
colour = modes[active_mode].colors[0];
}
controller->SetMode(modes[active_mode].value, speed, brightness, colour, random_colours);
controller->SetMode( modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, colour, random_colours);
}
@@ -18,6 +18,7 @@
#define CM_SMALL_ARGB_MIN_LEDS 4
#define CM_SMALL_ARGB_MAX_LEDS 48
#define CM_SMALL_ARGB_BRIGHTNESS_MAX 0xFF
#define CM_SMALL_ARGB_FW0012 "A202104052336"
class RGBController_CMSmallARGBController : public RGBController
{
@@ -25,9 +26,7 @@ public:
RGBController_CMSmallARGBController(CMSmallARGBController* controller_ptr);
~RGBController_CMSmallARGBController();
void SetupModes();
void SetupZones();
void DeviceConfigureZone(int zone_idx);
void DeviceUpdateLEDs();
@@ -36,7 +35,9 @@ public:
void SetCustomMode();
void DeviceUpdateMode();
private:
void Init_Controller();
int GetDeviceMode();
CMSmallARGBController* controller;
};
@@ -327,18 +327,9 @@ DetectedControllers DetectCoolerMasterSmallARGB(hid_device_info* info, const std
if(dev)
{
CMSmallARGBController* controller = new CMSmallARGBController(dev, info->path);
RGBController_CMSmallARGBController* rgb_controller = new RGBController_CMSmallARGBController(controller);
if(controller->GetVersion() != "Unsupported")
{
RGBController_CMSmallARGBController* rgb_controller = new RGBController_CMSmallARGBController(controller);
detected_controllers.push_back(rgb_controller);
}
else
{
LOG_ERROR("[CMSmallARGBController] Unsupported firmware version");
delete controller;
}
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
@@ -44,4 +44,8 @@ DetectedControllers DetectCorsairHydro2Controllers()
}
REGISTER_DETECTOR("Corsair H100i v2", DetectCorsairHydro2Controllers);
REGISTER_CUSTOM_UDEV_RULE(corsair_h100i_v2, "Corsair H100i v2", "SUBSYSTEMS==\"usb|hidraw\", ATTRS{idVendor}==\"1b1c\", ATTRS{idProduct}==\"0c09\", TAG+=\"uaccess\", TAG+=\"Corsair_H100i_v2\"");
/*---------------------------------------------------------------------------------------------------------*\
| Entries for dynamic UDEV rules |
| |
| DUMMY_DEVICE_DETECTOR("Corsair H100i v2", DetectCorsairHydro2Controllers, 0x1B1C, 0x0C09 ) |
\*---------------------------------------------------------------------------------------------------------*/
@@ -77,6 +77,10 @@ DetectedControllers DetectCorsairHydroControllers()
}
REGISTER_DETECTOR("Corsair Hydro Series", DetectCorsairHydroControllers);
REGISTER_CUSTOM_UDEV_RULE(corsair_hydro_1, "Corsair Hydro Series", "SUBSYSTEMS==\"usb|hidraw\", ATTRS{idVendor}==\"1b1c\", ATTRS{idProduct}==\"0c12\", TAG+=\"uaccess\", TAG+=\"Corsair_Hydro_Series\"");
REGISTER_CUSTOM_UDEV_RULE(corsair_hydro_2, "Corsair Hydro Series", "SUBSYSTEMS==\"usb|hidraw\", ATTRS{idVendor}==\"1b1c\", ATTRS{idProduct}==\"0c13\", TAG+=\"uaccess\", TAG+=\"Corsair_Hydro_Series\"");
REGISTER_CUSTOM_UDEV_RULE(corsair_hydro_3, "Corsair Hydro Series", "SUBSYSTEMS==\"usb|hidraw\", ATTRS{idVendor}==\"1b1c\", ATTRS{idProduct}==\"0c15\", TAG+=\"uaccess\", TAG+=\"Corsair_Hydro_Series\"");
/*---------------------------------------------------------------------------------------------------------*\
| Entries for dynamic UDEV rules |
| |
| DUMMY_DEVICE_DETECTOR("Corsair Hydro Series", DetectCorsairHydroControllers, 0x1B1C, 0x0C12 ) |
| DUMMY_DEVICE_DETECTOR("Corsair Hydro Series", DetectCorsairHydroControllers, 0x1B1C, 0x0C13 ) |
| DUMMY_DEVICE_DETECTOR("Corsair Hydro Series", DetectCorsairHydroControllers, 0x1B1C, 0x0C15 ) |
\*---------------------------------------------------------------------------------------------------------*/
@@ -232,7 +232,7 @@ void CorsairLightingNodeController::SendFirmwareRequest()
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
actual = WriteAndRead(usb_buf, CORSAIR_LIGHTING_NODE_READ_TIMEOUT);
actual = WriteAndRead(usb_buf);
if(actual > 0)
{
@@ -217,7 +217,7 @@ void CorsairPeripheralController::SetLEDsKeyboardFull(std::vector<RGBColor> colo
blu_val[keys_k95[color_idx]] = RGBGetBValue(color);
data_sz = 48; //untested
}
else if (logical_layout == CORSAIR_TYPE_K70_MK2 || logical_layout == CORSAIR_TYPE_K70_LUX_RGB)
else if (logical_layout == CORSAIR_TYPE_K70_MK2)
{
red_val[keys_k70_mk2[color_idx]] = RGBGetRValue(color);
grn_val[keys_k70_mk2[color_idx]] = RGBGetGValue(color);
@@ -447,7 +447,7 @@ void CorsairPeripheralController::SetupK55AndK95LightingControl()
if (physical_layout == CORSAIR_LAYOUT_ISO)
{
if(logical_layout == CORSAIR_TYPE_K70_MK2 || logical_layout == CORSAIR_TYPE_K70_LUX_RGB)
if(logical_layout == CORSAIR_TYPE_K70_MK2)
{
skipped_identifiers = key_mapping_k70_mk2_plat_iso;
skipped_identifiers_count = sizeof(key_mapping_k70_mk2_plat_iso) / sizeof(key_mapping_k70_mk2_plat_iso[0]);
@@ -632,15 +632,10 @@ void CorsairPeripheralController::ReadFirmwareInfo()
logical_layout = CORSAIR_TYPE_K55;
break;
case 0x1B33:
logical_layout = CORSAIR_TYPE_K70_LUX_RGB;
break;
case 0x1B38:
case 0x1B49:
case 0x1B6B:
case 0x1B55:
case 0x1B48:
logical_layout = CORSAIR_TYPE_K70_MK2;
break;
@@ -67,8 +67,7 @@ enum
CORSAIR_TYPE_K95 = 2,
CORSAIR_TYPE_K55 = 3,
CORSAIR_TYPE_K70_MK2 = 4,
CORSAIR_TYPE_K68 = 5,
CORSAIR_TYPE_K70_LUX_RGB = 6
CORSAIR_TYPE_K68 = 5
};
enum
@@ -48,7 +48,7 @@
#define CORSAIR_K70_RGB_MK2_PID 0x1B49
#define CORSAIR_K70_RGB_MK2_SE_PID 0x1B6B
#define CORSAIR_K70_RGB_MK2_LP_PID 0x1B55
#define CORSAIR_K70_RGB_PRO_OPTO_PID 0x1BC6
#define CORSAIR_K95_RGB_PID 0x1B11
#define CORSAIR_K95_PLATINUM_PID 0x1B2D
#define CORSAIR_K95_PLATINUM_SE_PID 0x1B82
@@ -81,7 +81,6 @@
#define CORSAIR_SCIMITAR_PRO_RGB_PID 0x1B3E
#define CORSAIR_SCIMITAR_ELITE_RGB_PID 0x1B8B
#define CORSAIR_SABRE_RGB_PID 0x1B2F
#define CORSAIR_SABRE_RGB_OPTICAL_PID 0x1B32
/*---------------------------------------------------------*\
| Mousepad product IDs |
@@ -193,7 +192,6 @@ REGISTER_HID_DETECTOR_IP("Corsair K70 RGB RAPIDFIRE", DetectCorsairPeriphe
REGISTER_HID_DETECTOR_IP("Corsair K70 RGB MK.2", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_RGB_MK2_PID, 1, 0xFFC2);
REGISTER_HID_DETECTOR_IP("Corsair K70 RGB MK.2 SE", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_RGB_MK2_SE_PID, 1, 0xFFC2);
REGISTER_HID_DETECTOR_IP("Corsair K70 RGB MK.2 Low Profile",DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_RGB_MK2_LP_PID, 1, 0xFFC2);
REGISTER_HID_DETECTOR_IP("Corsair K70 RGB PRO Optomechanical", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_RGB_PRO_OPTO_PID, 1, 0xFFC2);
REGISTER_HID_DETECTOR_IP("Corsair K95 RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K95_RGB_PID, 1, 0xFFC2);
REGISTER_HID_DETECTOR_IP("Corsair K95 RGB PLATINUM", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K95_PLATINUM_PID, 1, 0xFFC2);
REGISTER_HID_DETECTOR_IP("Corsair K95 RGB PLATINUM SE", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K95_PLATINUM_SE_PID, 1, 0xFFC2);
@@ -216,7 +214,6 @@ REGISTER_HID_DETECTOR_IP("Corsair Scimitar RGB", DetectCorsairPeriphe
REGISTER_HID_DETECTOR_IP("Corsair Scimitar PRO RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_SCIMITAR_PRO_RGB_PID, 1, 0xFFC2);
REGISTER_HID_DETECTOR_IP("Corsair Scimitar Elite RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_SCIMITAR_ELITE_RGB_PID, 1, 0xFFC2);
REGISTER_HID_DETECTOR_IP("Corsair Sabre RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_SABRE_RGB_PID, 1, 0xFFC2);
REGISTER_HID_DETECTOR_IP("Corsair Sabre RGB Optical", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_SABRE_RGB_OPTICAL_PID, 1, 0xFFC2);
/*---------------------------------------------------------*\
| Mousemats |
@@ -23,15 +23,6 @@ static unsigned int matrix_map[6][23] =
{ 4, 111, 22, 32, 40, 50, NA, 59, NA, 68, 78, 88, 97, 106, 61, NA, NA, 81, NA, 73, 83, 92, 109 },
{ 5, 14, 23, NA, NA, NA, NA, 41, NA, NA, NA, NA, 69, 79, 89, 71, 90, 99, 108, 101, NA, 110, NA } };
static unsigned int matrix_map_k70_lux_rgb[7][23] =
{ { NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 107, 8, NA, NA, 16, NA, NA,},
{ 0, NA, 10, 18, 28, 36, NA, 46, 55, 64, 74, NA, 84, 93, 102, 6, 15, 24, 33, 26, 35, 44, 53 },
{ 1, 11, 19, 29, 37, 47, NA, 56, 65, 75, 85, 94, 103, 7, 25, NA, 42, 51, 60, 62, 72, 82, 91 },
{ 2, NA, 12, 20, 30, 38, 48, 57, 66, 76, 86, NA, 95, 104, 70, 80, 34, 43, 52, 9, 17, 27, 100 },
{ 3, NA, 13, 21, 31, 39, 49, 58, 67, 77, 87, NA, 96, 105, 98, 112, NA, NA, NA, 45, 54, 63, NA },
{ 4, 111, NA, 22, 32, 40, 50, 59, 68, 78, 88, 97, NA, 106, 61, NA, NA, 81, NA, 73, 83, 92, 109 },
{ 5, NA, 14, 23, NA, NA, NA, 41, NA, NA, NA, 69, NA, 79, 89, 71, 90, 99, 108, 101, NA, 110, NA } };
static unsigned int matrix_map_k70_mk2[7][23] =
{ { NA, NA, NA, 115, 107, 8, NA, NA, NA, NA, NA, 113, 114, NA, NA, NA, NA, NA, NA, 16, NA, NA, NA,},
{ 0, NA, 10, 18, 28, 36, NA, 46, 55, 64, 74, NA, 84, 93, 102, 6, 15, 24, 33, 26, 35, 44, 53 },
@@ -77,24 +68,6 @@ static const zone_type zone_types[] =
ZONE_TYPE_MATRIX,
};
/*---------------------------------------------------------*\
| K70 LUX RGB Corsair Layout |
\*---------------------------------------------------------*/
static const char* zone_names_k70_lux_rgb[] =
{
ZONE_EN_KEYBOARD,
};
static const unsigned int zone_sizes_k70_lux_rgb[] =
{
116
};
static const zone_type zone_types_k70_lux_rgb[] =
{
ZONE_TYPE_MATRIX,
};
/*---------------------------------------------------------*\
| K70 MK2 Corsair Layout |
\*---------------------------------------------------------*/
@@ -186,7 +159,7 @@ static const char* led_names[] =
KEY_EN_LEFT_CONTROL, //5
KEY_EN_F12, //6
KEY_EN_EQUALS, //7
KEY_EN_WINDOWS_LOCK, //8
"Key: Lock", //8
KEY_EN_NUMPAD_7, //9
KEY_EN_F1, //12
KEY_EN_1, //13
@@ -285,7 +258,7 @@ static const char* led_names[] =
KEY_EN_LEFT_BRACKET, //134
KEY_EN_QUOTE, //135
KEY_EN_FORWARD_SLASH, //136
KEY_EN_BRIGHTNESS, //137
"Key: Brightness", //137
KEY_EN_RIGHT_ARROW, //139
KEY_EN_NUMPAD_ENTER, //140
KEY_EN_NUMPAD_PERIOD, //141
@@ -293,131 +266,6 @@ static const char* led_names[] =
KEY_EN_ISO_BACK_SLASH,
};
static const char* led_names_k70_lux_rgb[] =
{
KEY_EN_ESCAPE, //0
KEY_EN_BACK_TICK, //1
KEY_EN_TAB, //2
KEY_EN_CAPS_LOCK, //3
KEY_EN_LEFT_SHIFT, //4
KEY_EN_LEFT_CONTROL, //5
KEY_EN_F12, //6
KEY_EN_EQUALS, //7
KEY_EN_WINDOWS_LOCK, //8
KEY_EN_NUMPAD_7, //9
KEY_EN_F1, //12
KEY_EN_1, //13
KEY_EN_Q, //14
KEY_EN_A, //15
//"Key: / (ISO)", //16
KEY_EN_LEFT_WINDOWS, //17
KEY_EN_PRINT_SCREEN, //18
KEY_EN_MEDIA_MUTE, //20
KEY_EN_NUMPAD_8, //21
KEY_EN_F2, //24
KEY_EN_2, //25
KEY_EN_W, //26
KEY_EN_S, //27
KEY_EN_Z, //28
KEY_EN_LEFT_ALT, //29
KEY_EN_SCROLL_LOCK, //30
KEY_EN_BACKSPACE, //31
KEY_EN_MEDIA_STOP, //32
KEY_EN_NUMPAD_9, //33
KEY_EN_F3, //36
KEY_EN_3, //37
KEY_EN_E, //38
KEY_EN_D, //39
KEY_EN_X, //40
KEY_EN_PAUSE_BREAK, //42
KEY_EN_DELETE, //43
KEY_EN_MEDIA_PREVIOUS, //44
//"Key: Logo Left", //047
KEY_EN_F4, //48
KEY_EN_4, //49
KEY_EN_R, //50
KEY_EN_F, //51
KEY_EN_C, //52
KEY_EN_SPACE, //53
KEY_EN_INSERT, //54
KEY_EN_END, //55
KEY_EN_MEDIA_PLAY_PAUSE, //56
KEY_EN_NUMPAD_4, //57
//"Key: Logo Right", //059
KEY_EN_F5, //60
KEY_EN_5, //61
KEY_EN_T, //62
KEY_EN_G, //63
KEY_EN_V, //64
KEY_EN_HOME, //66
KEY_EN_PAGE_DOWN, //67
KEY_EN_MEDIA_NEXT, //68
KEY_EN_NUMPAD_5, //69
KEY_EN_F6, //72
KEY_EN_6, //73
KEY_EN_Y, //74
KEY_EN_H, //75
KEY_EN_B, //76
KEY_EN_PAGE_UP, //78
KEY_EN_RIGHT_SHIFT, //79
KEY_EN_NUMPAD_LOCK, //80
KEY_EN_NUMPAD_6, //81
KEY_EN_F7, //84
KEY_EN_7, //85
KEY_EN_U, //86
KEY_EN_J, //87
KEY_EN_N, //88
KEY_EN_RIGHT_ALT, //89
KEY_EN_RIGHT_BRACKET, //90
KEY_EN_RIGHT_CONTROL, //91
KEY_EN_NUMPAD_DIVIDE, //92
KEY_EN_NUMPAD_1, //93
KEY_EN_F8, //96
KEY_EN_8, //97
KEY_EN_I, //98
KEY_EN_K, //99
KEY_EN_M, //100
KEY_EN_RIGHT_WINDOWS, //101
KEY_EN_ANSI_BACK_SLASH, //102
KEY_EN_UP_ARROW, //103
KEY_EN_NUMPAD_TIMES, //104
KEY_EN_NUMPAD_2, //105
KEY_EN_F9, //108
KEY_EN_9, //109
KEY_EN_O, //110
KEY_EN_L, //111
KEY_EN_COMMA, //112
KEY_EN_MENU, //113
//KEY_EN_ISO_BACK_SLASH, //114
KEY_EN_LEFT_ARROW, //115
KEY_EN_NUMPAD_MINUS, //116
KEY_EN_NUMPAD_3, //117
KEY_EN_F10, //120
KEY_EN_0, //121
KEY_EN_P, //122
KEY_EN_SEMICOLON, //123
//"Key: Profile", //125
KEY_EN_PERIOD, //124
KEY_EN_ANSI_ENTER, //126
KEY_EN_DOWN_ARROW, //127
KEY_EN_NUMPAD_PLUS, //128
KEY_EN_NUMPAD_0, //129
KEY_EN_F11, //132
KEY_EN_MINUS, //133
KEY_EN_LEFT_BRACKET, //134
KEY_EN_QUOTE, //135
KEY_EN_FORWARD_SLASH, //136
KEY_EN_BRIGHTNESS, //137
KEY_EN_RIGHT_ARROW, //139
KEY_EN_NUMPAD_ENTER, //140
KEY_EN_NUMPAD_PERIOD, //141
"Key: / (ISO)", //16
KEY_EN_ISO_BACK_SLASH, //114
"Key: Logo Left", //047
"Key: Logo Right", //059
"Key: Profile", //125
};
static const char* led_names_k70_mk2[] =
{
KEY_EN_ESCAPE, //0
@@ -428,7 +276,7 @@ static const char* led_names_k70_mk2[] =
KEY_EN_LEFT_CONTROL, //5
KEY_EN_F12, //6
KEY_EN_EQUALS, //7
KEY_EN_WINDOWS_LOCK, //8
"Key: Lock", //8
KEY_EN_NUMPAD_7, //9
KEY_EN_F1, //12
KEY_EN_1, //13
@@ -532,7 +380,7 @@ static const char* led_names_k70_mk2[] =
KEY_EN_LEFT_BRACKET, //134
KEY_EN_QUOTE, //135
KEY_EN_FORWARD_SLASH, //136
KEY_EN_BRIGHTNESS, //137
"Key: Brightness", //137
KEY_EN_RIGHT_ARROW, //139
KEY_EN_NUMPAD_ENTER, //140
KEY_EN_NUMPAD_PERIOD, //141
@@ -553,7 +401,7 @@ static const char* led_names_k95_plat[] =
KEY_EN_LEFT_CONTROL, //5
KEY_EN_F12, //6
KEY_EN_EQUALS, //7
KEY_EN_WINDOWS_LOCK, //8
"Key: Lock", //8
KEY_EN_NUMPAD_7, //9
KEY_EN_F1, //12
KEY_EN_1, //13
@@ -652,7 +500,7 @@ static const char* led_names_k95_plat[] =
KEY_EN_LEFT_BRACKET, //134
KEY_EN_QUOTE, //135
KEY_EN_FORWARD_SLASH, //136
KEY_EN_BRIGHTNESS, //137
"Key: Brightness", //137
KEY_EN_RIGHT_ARROW, //139
KEY_EN_NUMPAD_ENTER, //140
KEY_EN_NUMPAD_PERIOD, //141
@@ -696,7 +544,7 @@ static const char* led_names_k95[] =
KEY_EN_LEFT_CONTROL, //5
KEY_EN_F12, //6
KEY_EN_EQUALS, //7
KEY_EN_WINDOWS_LOCK, //8
"Key: Lock", //8
KEY_EN_NUMPAD_7, //9
KEY_EN_F1, //12
KEY_EN_1, //13
@@ -795,7 +643,7 @@ static const char* led_names_k95[] =
KEY_EN_LEFT_BRACKET, //134
KEY_EN_QUOTE, //135
KEY_EN_FORWARD_SLASH, //136
KEY_EN_BRIGHTNESS, //137
"Key: Brightness", //137
KEY_EN_RIGHT_ARROW, //139
KEY_EN_NUMPAD_ENTER, //140
KEY_EN_NUMPAD_PERIOD, //141
@@ -1164,19 +1012,6 @@ void RGBController_CorsairPeripheral::SetupZones()
new_zone.leds_max = zone_sizes_k55[zone_idx];
new_zone.leds_count = zone_sizes_k55[zone_idx];
}
else if (logical_layout == CORSAIR_TYPE_K70_LUX_RGB)
{
new_zone.name = zone_names_k70_lux_rgb[zone_idx];
new_zone.type = zone_types_k70_lux_rgb[zone_idx];
new_zone.leds_min = zone_sizes_k70_lux_rgb[zone_idx];
new_zone.leds_max = zone_sizes_k70_lux_rgb[zone_idx];
new_zone.leds_count = zone_sizes_k70_lux_rgb[zone_idx];
if(zone_types[zone_idx] == ZONE_TYPE_MATRIX)
{
new_zone.matrix_map.Set(7, 23, (unsigned int *)&matrix_map_k70_lux_rgb);
}
}
else if (logical_layout == CORSAIR_TYPE_K70_MK2)
{
new_zone.name = zone_names_k70_mk2[zone_idx];
@@ -1266,10 +1101,6 @@ void RGBController_CorsairPeripheral::SetupZones()
{
new_led.name = led_names_k55[led_idx];
}
else if(logical_layout == CORSAIR_TYPE_K70_LUX_RGB)
{
new_led.name = led_names_k70_lux_rgb[led_idx];
}
else if(logical_layout == CORSAIR_TYPE_K70_MK2)
{
new_led.name = led_names_k70_mk2[led_idx];
@@ -9,9 +9,6 @@
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <chrono>
#include <thread>
#include "CorsairPeripheralV2Controller.h"
#include "StringUtils.h"
@@ -22,36 +19,21 @@ CorsairPeripheralV2Controller::CorsairPeripheralV2Controller(hid_device* dev_han
dev = dev_handle;
location = path;
device_name = name;
device_index = CORSAIR_V2_DEVICE_NOT_FOUND;
/*---------------------------------------------------------*\
| Get PID |
| If the PID is in the know wireless receivers list |
| switch the write_cmd to talk to the device and retry |
\*---------------------------------------------------------*/
/*---------------------------------------------------------*\
| Establish the packet size before talking to the device. |
\*---------------------------------------------------------*/
uint16_t report_size = GetOutputReportSize();
if(report_size >= CORSAIR_V2_WRITE_SIZE && report_size <= CORSAIR_V2_PACKET_SIZE)
{
pkt_sze = report_size;
}
LOG_DEBUG("[%s] Packet length set to %d", device_name.c_str(), pkt_sze);
unsigned int pid = GetAddressRetry(0x12);
unsigned int pid = GetAddress(0x12);
switch(pid)
{
case CORSAIR_SLIPSTREAM_WIRELESS_PID1:
case CORSAIR_SLIPSTREAM_WIRELESS_V2_PID1:
case CORSAIR_SLIPSTREAM_WIRELESS_PID2:
case CORSAIR_SLIPSTREAM_M75_PID:
case CORSAIR_SLIPSTREAM_IRONCLAW_SE_PID:
write_cmd = CORSAIR_V2_WRITE_WIRELESS_ID;
pid = GetAddressRetry(0x12);
pid = GetAddress(0x12);
break;
case CORSAIR_K57_RGB_WIRED_PID:
@@ -76,12 +58,14 @@ CorsairPeripheralV2Controller::CorsairPeripheralV2Controller(hid_device* dev_han
| the device being set up. |
\*---------------------------------------------------------*/
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
memset(buffer, 0, pkt_sze);
buffer[1] = write_cmd;
buffer[2] = CORSAIR_V2_CMD_GET;
buffer[3] = 0x11;
hid_write(dev, buffer, pkt_sze);
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
uint16_t result = hid_read_timeout(dev, buffer, CORSAIR_V2_PACKET_SIZE, CORSAIR_V2_TIMEOUT);
result++;
pkt_sze = std::max(result, (uint16_t)CORSAIR_V2_WRITE_SIZE);
LOG_DEBUG("[%s] Packet length set to %d", device_name.c_str(), pkt_sze);
/*---------------------------------------------------------*\
| NB: If the device is not found in the device list |
@@ -104,129 +88,28 @@ CorsairPeripheralV2Controller::CorsairPeripheralV2Controller(hid_device* dev_han
}
}
/*---------------------------------------------------------*\
| Unknown PID |
| Leave device_index at CORSAIR_V2_DEVICE_NOT_FOUND so |
| GetDeviceData() returns nullptr and the detector drops |
| the device, rather than building an RGBController on |
| top of an out of range index. |
\*---------------------------------------------------------*/
if(not_found)
{
LOG_ERROR("[%s] device capabilities not found. Please creata a new device request.",
device_name.c_str());
return;
}
}
/*---------------------------------------------------------*\
| Check lighting control endpoints |
| If lighting control endpoint 2 is unavailable |
| then use endpoint 1. |
\*---------------------------------------------------------*/
uint16_t CorsairPeripheralV2Controller::GetOutputReportSize()
{
unsigned char descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];
int length = hid_get_report_descriptor(dev, descriptor, sizeof(descriptor));
if(length <= 0)
{
return 0;
}
unsigned int report_size = 0;
unsigned int report_count = 0;
int index = 0;
while(index < length)
{
unsigned char item = descriptor[index];
unsigned char tag = item & 0xFC;
unsigned char item_size = item & 0x03;
/*---------------------------------------------------------*\
| A size field of 3 means four data bytes follow. |
\*---------------------------------------------------------*/
if(item_size == 3)
{
item_size = 4;
}
if(index + 1 + item_size > length)
{
break;
}
unsigned int value = 0;
for(unsigned char byte = 0; byte < item_size; byte++)
{
value |= (unsigned int)descriptor[index + 1 + byte] << (8 * byte);
}
switch(tag)
{
case 0x74: /* Report Size */
report_size = value;
break;
case 0x94: /* Report Count */
report_count = value;
break;
case 0x90: /* Output */
if(report_size > 0 && report_count > 0)
{
return (uint16_t)(((report_size * report_count) / 8) + 1);
}
break;
}
index += 1 + item_size;
}
return 0;
}
void CorsairPeripheralV2Controller::DetectLightingEndpoint()
{
/*---------------------------------------------------------*\
| A device that cannot be read from cannot be probed; those |
| carry the endpoint they need already set. |
\*---------------------------------------------------------*/
if(skip_reads)
{
return;
}
/*---------------------------------------------------------*\
| Lighting control 1 is the plain lighting resource and |
| takes colour block data. A device that answers |
| invalid or unsupported for it wants the alternate |
| lighting resource, which takes RGB triplets. |
| Check lighting control endpoints |
| If lighting control endpoint 2 is unavailable |
| then use endpoint 1. |
\*---------------------------------------------------------*/
light_ctrl = CORSAIR_V2_LIGHT_CTRL1;
unsigned char result = StartTransaction(0);
/*---------------------------------------------------------*\
| A handle left open by an earlier session answers failed; |
| close it and open it again. |
\*---------------------------------------------------------*/
if(result == CORSAIR_V2_ERR_FAILED)
if(light_ctrl == CORSAIR_V2_LIGHT_CTRL2)
{
result = StartTransaction(0);
if(result > 0)
{
light_ctrl = CORSAIR_V2_LIGHT_CTRL1;
StartTransaction(0);
}
StopTransaction(0);
result = StartTransaction(0);
LOG_DEBUG("[%s] Lighting Endpoint set to %02X", device_name.c_str(), light_ctrl);
}
if(result == CORSAIR_V2_ERR_INVALID || result == CORSAIR_V2_ERR_NOT_SUPPORTED)
{
light_ctrl = CORSAIR_V2_LIGHT_CTRL2;
StartTransaction(0);
}
StopTransaction(0);
LOG_DEBUG("[%s] Lighting Endpoint set to %02X", device_name.c_str(), light_ctrl);
}
CorsairPeripheralV2Controller::~CorsairPeripheralV2Controller()
@@ -236,19 +119,9 @@ CorsairPeripheralV2Controller::~CorsairPeripheralV2Controller()
const corsair_v2_device* CorsairPeripheralV2Controller::GetDeviceData()
{
if(!IsDeviceSupported())
{
return nullptr;
}
return corsair_v2_device_list[device_index];
}
bool CorsairPeripheralV2Controller::IsDeviceSupported()
{
return device_index < CORSAIR_V2_DEVICE_COUNT;
}
std::string CorsairPeripheralV2Controller::GetDeviceLocation()
{
return("HID: " + location);
@@ -294,9 +167,9 @@ std::string CorsairPeripheralV2Controller::GetSerialString()
void CorsairPeripheralV2Controller::SetRenderMode(corsair_v2_device_mode mode)
{
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
uint8_t buffer[CORSAIR_V2_WRITE_SIZE];
memset(buffer, 0, pkt_sze);
memset(buffer, 0, CORSAIR_V2_WRITE_SIZE);
/*---------------------------------------------------------*\
| Set Mode |
@@ -306,19 +179,19 @@ void CorsairPeripheralV2Controller::SetRenderMode(corsair_v2_device_mode mode)
buffer[3] = CORSAIR_V2_VALUE_MODE;
buffer[5] = mode;
hid_write(dev, buffer, pkt_sze);
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
if(!skip_reads)
{
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
}
}
void CorsairPeripheralV2Controller::LightingControl(uint8_t opt1)
{
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
uint8_t buffer[CORSAIR_V2_WRITE_SIZE];
memset(buffer, 0, pkt_sze);
memset(buffer, 0, CORSAIR_V2_WRITE_SIZE);
/*---------------------------------------------------------*\
| The Corsair command is the same for each initialisation |
@@ -330,11 +203,11 @@ void CorsairPeripheralV2Controller::LightingControl(uint8_t opt1)
buffer[3] = opt1;
buffer[5] = 0x00;
hid_write(dev, buffer, pkt_sze);
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
if(!skip_reads)
{
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
}
}
@@ -343,48 +216,20 @@ unsigned int CorsairPeripheralV2Controller::GetKeyboardLayout()
return GetAddress(0x41);
}
/*---------------------------------------------------------*\
| GetAddress returns NA when the device answers with a |
| protocol error, and a receiver whose device has not |
| woken answers zero. Straight after enumeration both |
| are normal, so poll before treating either as final. |
\*---------------------------------------------------------*/
unsigned int CorsairPeripheralV2Controller::GetAddressRetry(uint8_t address)
{
unsigned int value = NA;
for(unsigned int attempt = 0; attempt < CORSAIR_V2_PID_RETRIES; attempt++)
{
value = GetAddress(address);
if(value != NA && value != 0)
{
return value;
}
if(attempt + 1 < CORSAIR_V2_PID_RETRIES)
{
std::this_thread::sleep_for(std::chrono::milliseconds(CORSAIR_V2_PID_RETRY_DELAY));
}
}
return value;
}
unsigned int CorsairPeripheralV2Controller::GetAddress(uint8_t address)
{
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
uint8_t read[CORSAIR_V2_PACKET_SIZE];
uint8_t buffer[CORSAIR_V2_WRITE_SIZE];
uint8_t read[CORSAIR_V2_WRITE_SIZE];
memset(buffer, 0, pkt_sze);
memset(read, 0, pkt_sze);
memset(buffer, 0, CORSAIR_V2_WRITE_SIZE);
memset(read, 0, CORSAIR_V2_WRITE_SIZE);
buffer[1] = write_cmd;
buffer[2] = CORSAIR_V2_CMD_GET;
buffer[3] = address;
hid_write(dev, buffer, pkt_sze);
hid_read_timeout(dev, read, pkt_sze, CORSAIR_V2_TIMEOUT);
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
hid_read_timeout(dev, read, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
unsigned int temp = (unsigned int)(read[6] << 24 | read[5] << 16 | read[4] << 8 | read[3]);
LOG_DEBUG("[%s] GetAddress %02X - %02X %02X - %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(),
@@ -402,20 +247,20 @@ unsigned int CorsairPeripheralV2Controller::GetAddress(uint8_t address)
unsigned char CorsairPeripheralV2Controller::StartTransaction(uint8_t opt1)
{
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
uint8_t buffer[CORSAIR_V2_WRITE_SIZE];
memset(buffer, 0, pkt_sze);
memset(buffer, 0, CORSAIR_V2_WRITE_SIZE);
buffer[1] = write_cmd;
buffer[2] = CORSAIR_V2_CMD_START_TX;
buffer[3] = opt1;
buffer[4] = light_ctrl;
hid_write(dev, buffer, pkt_sze);
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
if(!skip_reads)
{
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
}
return buffer[2];
@@ -423,20 +268,20 @@ unsigned char CorsairPeripheralV2Controller::StartTransaction(uint8_t opt1)
void CorsairPeripheralV2Controller::StopTransaction(uint8_t opt1)
{
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
uint8_t buffer[CORSAIR_V2_WRITE_SIZE];
memset(buffer, 0, pkt_sze);
memset(buffer, 0, CORSAIR_V2_WRITE_SIZE);
buffer[1] = write_cmd;
buffer[2] = CORSAIR_V2_CMD_STOP_TX;
buffer[3] = 0x01;
buffer[4] = opt1;
hid_write(dev, buffer, pkt_sze);
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
if(!skip_reads)
{
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
}
}
@@ -450,11 +295,6 @@ void CorsairPeripheralV2Controller::ClearPacketBuffer()
uint8_t result = 0;
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
/*---------------------------------------------------------*\
| Draining expects to find nothing, so the last read here |
| always waits out the timeout. Keep it short: this runs |
| before every LED update. |
\*---------------------------------------------------------*/
do
{
result = hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT);
@@ -464,8 +304,6 @@ void CorsairPeripheralV2Controller::ClearPacketBuffer()
void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size)
{
std::lock_guard<std::mutex> lock(device_mutex);
const uint8_t offset1 = 8;
const uint8_t offset2 = 4;
uint16_t remaining = data_size;
@@ -499,7 +337,7 @@ void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size)
if(!skip_reads)
{
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT);
}
remaining -= copy_bytes;
@@ -524,7 +362,7 @@ void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size)
if(!skip_reads)
{
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT);
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT);
}
remaining -= copy_bytes;
@@ -536,8 +374,6 @@ void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size)
void CorsairPeripheralV2Controller::UpdateHWMode(uint16_t mode, corsair_v2_color /*color_mode*/, uint8_t /*speed*/,
uint8_t /*direction*/, uint8_t /*brightness*/, std::vector<RGBColor> /*colors*/)
{
std::lock_guard<std::mutex> lock(device_mutex);
/*---------------------------------------------------------*\
| If we are switching to `Direct` mode |
| set device in software mode |
@@ -12,7 +12,6 @@
#pragma once
#include <string>
#include <mutex>
#include <vector>
#include <hidapi.h>
#include "LogManager.h"
@@ -22,33 +21,13 @@
#define NA 0xFFFFFFFF
#define HID_MAX_STR 255
/*---------------------------------------------------------*\
| A jumbo packet device answers a 1KB command in well over |
| 50ms. Missing the reply leaves it queued and the next |
| write to the device then fails outright, so this has to |
| be generous. A reply that does arrive is returned as |
| soon as it lands, so this only costs time on failure. |
\*---------------------------------------------------------*/
#define CORSAIR_V2_TIMEOUT 500
#define CORSAIR_V2_TIMEOUT 50
#define CORSAIR_V2_TIMEOUT_SHORT 3
#define CORSAIR_V2_VALUE_MODE 3
#define CORSAIR_V2_WRITE_WIRED_ID 8
#define CORSAIR_V2_WRITE_WIRELESS_ID 9
#define CORSAIR_V2_WRITE_SIZE 65
/*---------------------------------------------------------*\
| Largest packet the protocol uses: 1024 payload bytes |
| plus the leading report ID. pkt_sze is measured |
| against this, and every stack buffer handed to hidapi |
| with pkt_sze is this size, so the two must agree. |
\*---------------------------------------------------------*/
#define CORSAIR_V2_PACKET_SIZE 1025
/*---------------------------------------------------------*\
| Error codes the device returns in byte 2 of a reply. |
\*---------------------------------------------------------*/
#define CORSAIR_V2_ERR_INVALID 0x01
#define CORSAIR_V2_ERR_FAILED 0x03
#define CORSAIR_V2_ERR_NOT_SUPPORTED 0x06
#define CORSAIR_V2_PACKET_SIZE 1024
#define CORSAIR_V2_LIGHT_CTRL1 1
#define CORSAIR_V2_LIGHT_CTRL2 34 /* 0x22 */
@@ -58,19 +37,6 @@
#define CORSAIR_V2_BRIGHTNESS_MIN 0
#define CORSAIR_V2_BRIGHTNESS_MAX 0xFF
/*---------------------------------------------------------*\
| A device that has just enumerated does not answer the PID |
| query straight away, so retry before giving up on it. |
\*---------------------------------------------------------*/
#define CORSAIR_V2_PID_RETRIES 8
#define CORSAIR_V2_PID_RETRY_DELAY 120
/*---------------------------------------------------------*\
| device_index when the PID is not in the device list. |
| Never index the device list with it. |
\*---------------------------------------------------------*/
#define CORSAIR_V2_DEVICE_NOT_FOUND 0xFFFF
enum corsair_v2_cmd
{
CORSAIR_V2_CMD_SET = 0x01, /* Command for setting values */
@@ -118,7 +84,6 @@ public:
std::string GetName();
std::string GetSerialString();
const corsair_v2_device* GetDeviceData();
bool IsDeviceSupported();
unsigned int GetKeyboardLayout();
void SetRenderMode(corsair_v2_device_mode mode);
@@ -134,46 +99,14 @@ protected:
std::string device_name;
uint8_t light_ctrl = CORSAIR_V2_LIGHT_CTRL2;
/*---------------------------------------------------------*\
| Works out which lighting control endpoint the device |
| accepts. Must be called only once the device has been |
| put into software render mode: endpoint 2 rejects the |
| transaction while the device is still driving its own |
| lighting, which would pick endpoint 1 for a device that |
| does not support it. |
\*---------------------------------------------------------*/
void DetectLightingEndpoint();
/*---------------------------------------------------------*\
| Size of one output report, plus the leading report ID. |
| Read from the HID report descriptor before anything is |
| sent: these devices come in a 64 byte and a 1024 byte |
| flavour, and sending a short packet to a 1024 byte |
| endpoint wedges it until the device is reset, so the |
| size has to be known before the first write rather than |
| inferred from a reply. Returns 0 if it cannot be read. |
\*---------------------------------------------------------*/
uint16_t GetOutputReportSize();
private:
void ClearPacketBuffer();
unsigned int GetAddress(uint8_t address);
unsigned int GetAddressRetry(uint8_t address);
unsigned char StartTransaction(uint8_t opt1);
void StopTransaction(uint8_t opt1);
hid_device* dev;
/*---------------------------------------------------------*\
| Serialises device transactions. |
| The RGB controllers run a keepalive thread that writes |
| LEDs independently of the caller, and a Corsair block |
| write spans several packets. Letting two of those |
| interleave on one HID handle corrupts the transaction |
| and leaves the device unresponsive until it is reset. |
\*---------------------------------------------------------*/
std::mutex device_mutex;
uint8_t write_cmd = CORSAIR_V2_WRITE_WIRED_ID;
uint16_t pkt_sze = CORSAIR_V2_WRITE_SIZE;
bool skip_reads = false;
@@ -12,7 +12,6 @@
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "LogManager.h"
/*---------------------------------------------------------*\
| Corsair Peripheral specific includes |
@@ -38,20 +37,6 @@ DetectedControllers DetectCorsairV2HardwareControllers(hid_device_info* info, co
if(dev)
{
CorsairPeripheralV2HWController* controller = new CorsairPeripheralV2HWController(dev, info->path, name);
/*---------------------------------------------------------*\
| A device whose PID is not in the device list has no |
| zone or LED data to build an RGBController from. |
| Drop it here: building one anyway indexes the device |
| list with an index that was never set. |
\*---------------------------------------------------------*/
if(!controller->IsDeviceSupported())
{
LOG_WARNING("[%s] not registering device, capabilities unknown.", name.c_str());
delete controller;
return(detected_controllers);
}
RGBController_CorsairV2HW* rgb_controller = new RGBController_CorsairV2HW(controller);
detected_controllers.push_back(rgb_controller);
@@ -70,14 +55,6 @@ DetectedControllers DetectCorsairV2SoftwareControllers(hid_device_info* info, co
if(dev)
{
CorsairPeripheralV2SWController* controller = new CorsairPeripheralV2SWController(dev, info->path, name);
if(!controller->IsDeviceSupported())
{
LOG_WARNING("[%s] not registering device, capabilities unknown.", name.c_str());
delete controller;
return(detected_controllers);
}
RGBController_CorsairV2SW* rgb_controller = new RGBController_CorsairV2SW(controller);
detected_controllers.push_back(rgb_controller);
@@ -92,7 +69,6 @@ DetectedControllers DetectCorsairV2SoftwareControllers(hid_device_info* info, co
REGISTER_HID_DETECTOR_IP("Corsair K55 RGB PRO", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K55_RGB_PRO_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K57 RGB (Wired)", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K57_RGB_WIRED_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO SE", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_SE_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO Low Profile", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_LP_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO TKL Black", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_TKL_B_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO TKL White", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_TKL_W_PID, 1, 0xFF42);
@@ -115,8 +91,6 @@ REGISTER_HID_DETECTOR_IP("Corsair Dark Core RGB SE (Wired)", DetectCorsai
REGISTER_HID_DETECTOR_IP("Corsair Dark Core RGB Pro SE (Wired)", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_DARK_CORE_RGB_PRO_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair Harpoon Wireless (Wired)", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_HARPOON_WIRELESS_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair Ironclaw Wireless (Wired)", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_IRONCLAW_WIRELESS_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair Ironclaw Wireless SE (Wired)", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_IRONCLAW_WIRELESS_SE_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair Ironclaw Wireless SE", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_SLIPSTREAM_IRONCLAW_SE_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair Katar Pro", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_KATAR_PRO_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair Katar Pro V2", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_KATAR_PRO_V2_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair Katar Pro XT", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_KATAR_PRO_XT_PID, 1, 0xFF42);
@@ -124,8 +98,6 @@ REGISTER_HID_DETECTOR_IP("Corsair M55 RGB PRO", DetectCorsai
REGISTER_HID_DETECTOR_IP("Corsair M65 RGB Ultra Wired", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_M65_RGB_ULTRA_WIRED_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair M65 RGB Ultra Wireless (Wired)", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_M65_RGB_ULTRA_WIRELESS_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair M75 Gaming Mouse", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_M75_GAMING_MOUSE_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair M75 Wireless", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_SLIPSTREAM_M75_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair M75 Wireless (Wired)", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_M75_WIRELESS_PID, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair Slipstream Wireless Receiver HW", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_SLIPSTREAM_WIRELESS_PID1, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair Slipstream Wireless Receiver SW", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_SLIPSTREAM_WIRELESS_PID2, 1, 0xFF42);
REGISTER_HID_DETECTOR_IP("Corsair Slipstream Wireless Receiver HW", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_SLIPSTREAM_WIRELESS_V2_PID1, 1, 0xFF42);
@@ -638,71 +638,6 @@ static const corsair_v2_device ironclaw_wireless_device =
nullptr
};
/*-------------------------------------------------------------*\
| Corsair Ironclaw Wireless SE 1B1C:2B32 |
| |
| Zone "Logo" |
| Single |
| |
| Zone "Scroll Wheel" |
| Single |
| |
| Zone "Buttons" |
| Single |
| |
| Zone "Side" |
| Linear |
| 1 Row, 3 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone ironclaw_se_logo_zone =
{
"Logo",
ZONE_TYPE_SINGLE,
1,
1
};
static const corsair_v2_zone ironclaw_se_scroll_zone =
{
"Scroll Wheel",
ZONE_TYPE_SINGLE,
1,
1
};
static const corsair_v2_zone ironclaw_se_button_zone =
{
"Buttons",
ZONE_TYPE_SINGLE,
1,
1
};
static const corsair_v2_zone ironclaw_se_side_zone =
{
"Side Zone",
ZONE_TYPE_LINEAR,
1,
3
};
static const corsair_v2_device ironclaw_wireless_se_device =
{
CORSAIR_IRONCLAW_WIRELESS_SE_PID,
DEVICE_TYPE_MOUSE,
1,
6,
{
&ironclaw_se_logo_zone,
&ironclaw_se_scroll_zone,
&ironclaw_se_button_zone,
&ironclaw_se_side_zone,
nullptr,
nullptr
},
nullptr
};
/*-------------------------------------------------------------*\
| Corsair Katar Pro 1B1C:1B93 |
| |
@@ -901,7 +836,7 @@ static const corsair_v2_device k57_rgb_wired_device =
| |
| Zone "Keyboard" |
| Matrix |
| 7 Rows, 22 Columns |
| 6 Rows, 21 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone k60_rgb_pro_zone =
{
@@ -928,34 +863,12 @@ static const corsair_v2_device k60_rgb_pro_device =
&corsair_K60_layout
};
/*-------------------------------------------------------------*\
| Corsair K60 RGB Pro SE 1B1C:1B8D |
| |
| Initial implementation reuses K60 RGB Pro zone/layout |
\*-------------------------------------------------------------*/
static const corsair_v2_device k60_rgb_pro_se_device =
{
CORSAIR_K60_RGB_PRO_SE_PID,
DEVICE_TYPE_KEYBOARD,
6,
21,
{
&k60_rgb_pro_zone,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr
},
&corsair_K60_layout
};
/*-------------------------------------------------------------*\
| Corsair K60 RGB Pro Low Profile 1B1C:1BAD |
| |
| Zone "Keyboard" |
| Matrix |
| 7 Rows, 22 Columns |
| 6 Rows, 21 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone k60_rgb_pro_lp_zone =
{
@@ -988,7 +901,7 @@ static const corsair_v2_device k60_rgb_pro_lp_device =
| |
| Zone "Keyboard" |
| Matrix |
| 7 Rows, 22 Columns |
| 6 Rows, 21 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone k60_rgb_pro_tkl_zone =
{
@@ -1038,7 +951,7 @@ static const corsair_v2_device k60_rgb_pro_tkl_device_w =
| |
| Zone "Keyboard" |
| Matrix |
| 7 Rows, 22 Columns |
| 6 Rows, 21 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone k70_core_rgb_zone =
{
@@ -1167,14 +1080,14 @@ static const corsair_v2_device k70_rgb_tkl_cs_device =
| |
| Zone "Keyboard" |
| Matrix |
| 7 Rows, 22 Columns |
| 6 Rows, 21 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_zone k70_rgb_pro_zone =
{
ZONE_EN_KEYBOARD,
ZONE_TYPE_MATRIX,
7,
22
21
};
static const corsair_v2_device k70_rgb_pro_device =
@@ -1182,7 +1095,7 @@ static const corsair_v2_device k70_rgb_pro_device =
CORSAIR_K70_RGB_PRO_PID,
DEVICE_TYPE_KEYBOARD,
7,
22,
21,
{
&k70_rgb_pro_zone,
nullptr,
@@ -1199,14 +1112,14 @@ static const corsair_v2_device k70_rgb_pro_device =
| |
| Zone "Keyboard" |
| Matrix |
| 7 Rows, 22 Columns |
| 6 Rows, 21 Columns |
\*-------------------------------------------------------------*/
static const corsair_v2_device k70_rgb_pro_v2_device =
{
CORSAIR_K70_RGB_PRO_V2_PID,
DEVICE_TYPE_KEYBOARD,
7,
22,
21,
{
&k70_rgb_pro_zone,
nullptr,
@@ -1215,8 +1128,7 @@ static const corsair_v2_device k70_rgb_pro_v2_device =
nullptr,
nullptr
},
&corsair_k70_pro_layout,
193
&corsair_k70_pro_layout
};
/*-------------------------------------------------------------*\
@@ -1531,44 +1443,6 @@ static const corsair_v2_device m75_device =
nullptr
};
/*-------------------------------------------------------------*\
| Corsair M75 Wireless 1B1C:1BF1 |
| |
| Reached through the Slipstream receiver 1B1C:1BDC, which |
| reports the mouse PID 0x1BF1 once the write command is |
| switched to the wireless ID. |
| |
| Zone "Logo" |
| Single |
| |
| Zone "Bottom" |
| Single |
\*-------------------------------------------------------------*/
static const corsair_v2_zone m75_wireless_bottom_zone =
{
"Bottom",
ZONE_TYPE_SINGLE,
1,
1
};
static const corsair_v2_device m75_wireless_device =
{
CORSAIR_M75_WIRELESS_PID,
DEVICE_TYPE_MOUSE,
1,
2,
{
&m75_logo_zone,
&m75_wireless_bottom_zone,
nullptr,
nullptr,
nullptr,
nullptr
},
nullptr
};
/*-------------------------------------------------------------*\
| Corsair MM700 1B1C:1B9B |
| |
@@ -1648,7 +1522,6 @@ const corsair_v2_device* corsair_v2_device_list_data[] =
&k55_rgb_pro_device,
&k57_rgb_wired_device,
&k60_rgb_pro_device,
&k60_rgb_pro_se_device,
&k60_rgb_pro_lp_device,
&k60_rgb_pro_tkl_device_b,
&k60_rgb_pro_tkl_device_w,
@@ -1670,7 +1543,6 @@ const corsair_v2_device* corsair_v2_device_list_data[] =
&dark_core_pro_se_device,
&harpoon_wireless_device,
&ironclaw_wireless_device,
&ironclaw_wireless_se_device,
&katar_pro_device,
&katar_pro_v2_device,
&katar_pro_xt_device,
@@ -1678,7 +1550,6 @@ const corsair_v2_device* corsair_v2_device_list_data[] =
&m65_rgb_ultra_wired_device,
&m65_ultra_rgb_device,
&m75_device,
&m75_wireless_device,
/*-----------------------------------------------------------------*\
| MOUSEMATS |
@@ -63,15 +63,6 @@ typedef struct
uint8_t cols;
const corsair_v2_zone* zones[CORSAIR_ZONES_MAX];
keyboard_keymap_overlay_values* layout_new;
/*-------------------------------------------------------*\
| Number of LED slots the device expects in a direct |
| lighting write. The keymap only covers the keys that |
| exist, but the device wants its full slot count and |
| ignores a short write, so the buffer map is padded up |
| to this. Left 0 where the count is not known, which |
| keeps the keymap derived size. |
\*-------------------------------------------------------*/
uint16_t hw_led_count;
} corsair_v2_device;
/*-----------------------------------------------------*\
@@ -81,7 +72,6 @@ typedef struct
#define CORSAIR_K57_RGB_WIRED_PID 0x1B6E
#define CORSAIR_K57_RGB_WIRELESS_PID 0x1B62
#define CORSAIR_K60_RGB_PRO_PID 0x1BA0
#define CORSAIR_K60_RGB_PRO_SE_PID 0x1B8D
#define CORSAIR_K60_RGB_PRO_LP_PID 0x1BAD
#define CORSAIR_K60_RGB_PRO_TKL_B_PID 0x1BC7
#define CORSAIR_K60_RGB_PRO_TKL_W_PID 0x1BED
@@ -103,8 +93,6 @@ typedef struct
#define CORSAIR_DARK_CORE_RGB_PRO_PID 0x1B7E
#define CORSAIR_HARPOON_WIRELESS_PID 0x1B5E
#define CORSAIR_IRONCLAW_WIRELESS_PID 0x1B4C
#define CORSAIR_IRONCLAW_WIRELESS_SE_PID 0x2B32
#define CORSAIR_SLIPSTREAM_IRONCLAW_SE_PID 0x2B00
#define CORSAIR_KATAR_PRO_PID 0x1B93
#define CORSAIR_KATAR_PRO_V2_PID 0x1BBA
#define CORSAIR_KATAR_PRO_XT_PID 0x1BAC
@@ -112,8 +100,6 @@ typedef struct
#define CORSAIR_M65_RGB_ULTRA_WIRED_PID 0x1B9E
#define CORSAIR_M65_RGB_ULTRA_WIRELESS_PID 0x1BB5
#define CORSAIR_M75_GAMING_MOUSE_PID 0x1BF0
#define CORSAIR_M75_WIRELESS_PID 0x1BF1
#define CORSAIR_SLIPSTREAM_M75_PID 0x1BDC
#define CORSAIR_SLIPSTREAM_WIRELESS_PID1 0x1BA6
#define CORSAIR_SLIPSTREAM_WIRELESS_V2_PID1 0x1B66
#define CORSAIR_SLIPSTREAM_WIRELESS_PID2 0x1B65
@@ -16,12 +16,6 @@ CorsairPeripheralV2HWController::CorsairPeripheralV2HWController(hid_device* dev
{
SetRenderMode(CORSAIR_V2_MODE_SW);
LightingControl(0x5F);
/*---------------------------------------------------------*\
| Only now that the device is in software render mode can |
| its lighting control endpoint be probed reliably. |
\*---------------------------------------------------------*/
DetectLightingEndpoint();
}
CorsairPeripheralV2HWController::~CorsairPeripheralV2HWController()
@@ -16,12 +16,6 @@ CorsairPeripheralV2SWController::CorsairPeripheralV2SWController(hid_device* dev
{
SetRenderMode(CORSAIR_V2_MODE_SW);
LightingControl(0x5F);
/*---------------------------------------------------------*\
| Only now that the device is in software render mode can |
| its lighting control endpoint be probed reliably. |
\*---------------------------------------------------------*/
DetectLightingEndpoint();
}
CorsairPeripheralV2SWController::~CorsairPeripheralV2SWController()
@@ -66,32 +66,21 @@ RGBController_CorsairV2HW::RGBController_CorsairV2HW(CorsairPeripheralV2Controll
| to not revert back into rainbow mode. Start a thread |
| to continuously send a keepalive packet every 50 sec |
\*-----------------------------------------------------*/
/*-----------------------------------------------------*\
| Start the keepalive clock before the thread runs. |
| A default constructed time_point sits at the clock |
| epoch, which is always older than the update |
| period, so the thread would otherwise fire a write |
| on its first pass - during detection, racing |
| whatever the caller is doing. |
\*-----------------------------------------------------*/
last_update_time = std::chrono::steady_clock::now();
keepalive_thread_run = true;
keepalive_thread = new std::thread(&RGBController_CorsairV2HW::KeepaliveThread, this);
}
RGBController_CorsairV2HW::~RGBController_CorsairV2HW()
{
Shutdown();
/*-----------------------------------------------------*\
| Close keepalive thread first, so that it cannot issue |
| a write while the device is being shut down. |
| Close keepalive thread |
\*-----------------------------------------------------*/
keepalive_thread_run = false;
keepalive_thread->join();
delete keepalive_thread;
Shutdown();
delete controller;
}
@@ -209,15 +198,6 @@ void RGBController_CorsairV2HW::SetupZones()
SetupColors();
/*---------------------------------------------------------*\
| Pad out to the slot count the device expects. A write |
| that is short of it is ignored by the hardware. |
\*---------------------------------------------------------*/
if(corsair->hw_led_count > max_led_value)
{
max_led_value = corsair->hw_led_count;
}
/*---------------------------------------------------------*\
| Create a buffer map of pointers which contains the |
| layout order of colors the device expects. |
@@ -53,32 +53,21 @@ RGBController_CorsairV2SW::RGBController_CorsairV2SW(CorsairPeripheralV2Controll
| to not revert back into rainbow mode. Start a thread |
| to continuously send a keepalive packet every 50 sec |
\*-----------------------------------------------------*/
/*-----------------------------------------------------*\
| Start the keepalive clock before the thread runs. |
| A default constructed time_point sits at the clock |
| epoch, which is always older than the update |
| period, so the thread would otherwise fire a write |
| on its first pass - during detection, racing |
| whatever the caller is doing. |
\*-----------------------------------------------------*/
last_update_time = std::chrono::steady_clock::now();
keepalive_thread_run = true;
keepalive_thread = new std::thread(&RGBController_CorsairV2SW::KeepaliveThread, this);
}
RGBController_CorsairV2SW::~RGBController_CorsairV2SW()
{
Shutdown();
/*-----------------------------------------------------*\
| Close keepalive thread first, so that it cannot issue |
| a write while the device is being shut down. |
| Close keepalive thread |
\*-----------------------------------------------------*/
keepalive_thread_run = false;
keepalive_thread->join();
delete keepalive_thread;
Shutdown();
delete controller;
}
@@ -196,15 +185,6 @@ void RGBController_CorsairV2SW::SetupZones()
SetupColors();
/*---------------------------------------------------------*\
| Pad out to the slot count the device expects. A write |
| that is short of it is ignored by the hardware. |
\*---------------------------------------------------------*/
if(corsair->hw_led_count > max_led_value)
{
max_led_value = corsair->hw_led_count;
}
/*---------------------------------------------------------*\
| Create a buffer map of pointers which contains the |
| layout order of colors the device expects. |
@@ -1,7 +1,7 @@
/*---------------------------------------------------------*\
| CreativeSoundBlasterXG6ControllerDetect.cpp |
| CreativeControllerDetect.cpp |
| |
| Detector for Creative Sound Blaster XG6 devices |
| Detector for Creative devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
@@ -15,14 +15,14 @@
/*---------------------------------------------------------*\
| Creative vendor ID |
\*---------------------------------------------------------*/
#define CREATIVE_VID 0x041E
#define CREATIVE_VID 0x041E
/*---------------------------------------------------------*\
| Creative Sound Blaster XG6 product ID |
| SoundCards |
\*---------------------------------------------------------*/
#define CREATIVE_SOUNDBLASTERX_G6_PID 0x3256
#define CREATIVE_SOUNDBLASTERX_G6_PID 0x3256
DetectedControllers DetectCreativeSoundBlasterXG6Device(hid_device_info* info, const std::string& name)
DetectedControllers DetectCreativeDevice(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
@@ -40,7 +40,7 @@ DetectedControllers DetectCreativeSoundBlasterXG6Device(hid_device_info* info, c
return(detected_controllers);
}
/*---------------------------------------------------------*\
| Sound Cards |
\*---------------------------------------------------------*/
REGISTER_HID_DETECTOR_I("Creative SoundBlasterX G6", DetectCreativeSoundBlasterXG6Device, CREATIVE_VID, CREATIVE_SOUNDBLASTERX_G6_PID, 4);
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
| Sound Cards |
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
REGISTER_HID_DETECTOR_I("Creative SoundBlasterX G6", DetectCreativeDevice, CREATIVE_VID, CREATIVE_SOUNDBLASTERX_G6_PID, 4);
@@ -16,10 +16,13 @@ DetectedControllers DetectCreativeAE5Device()
{
DetectedControllers detected_controllers;
LOG_INFO("[Creative SoundBlaster AE-5] Windows detection function called");
CreativeSoundBlasterAE5Controller_Windows* controller = new CreativeSoundBlasterAE5Controller_Windows();
if(controller->Initialize())
{
LOG_INFO("[Creative SoundBlaster AE-5] Device initialized successfully, registering controller");
RGBController_CreativeSoundBlasterAE5* rgb_controller = new RGBController_CreativeSoundBlasterAE5(controller);
detected_controllers.push_back(rgb_controller);
+10 -60
View File
@@ -59,57 +59,15 @@ RGBController_DDP::~RGBController_DDP()
void RGBController_DDP::SetupZones()
{
/*-----------------------------------------------------*\
| Only set LED count on the first run |
\------------------------------------------------------*/
bool first_run = false;
if(zones.size() == 0)
for(unsigned int zone_idx = 0; zone_idx < devices.size(); zone_idx++)
{
first_run = true;
}
/*-----------------------------------------------------*\
| Clear any existing color/LED configuration |
\------------------------------------------------------*/
leds.clear();
colors.clear();
zones.resize(devices.size());
/*-----------------------------------------------------*\
| Set zones and leds |
\------------------------------------------------------*/
for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
zones[zone_idx].leds_min = devices[zone_idx].num_leds;
zones[zone_idx].leds_max = devices[zone_idx].num_leds;
if(first_run)
{
zones[zone_idx].flags = ZONE_FLAG_MANUALLY_CONFIGURABLE_NAME
| ZONE_FLAG_MANUALLY_CONFIGURABLE_TYPE
| ZONE_FLAG_MANUALLY_CONFIGURABLE_MATRIX_MAP
| ZONE_FLAG_MANUALLY_CONFIGURABLE_SEGMENTS;
}
if(!(zones[zone_idx].flags & ZONE_FLAG_MANUALLY_CONFIGURED_NAME))
{
zones[zone_idx].name = devices[zone_idx].name;
}
zones[zone_idx].leds_count = devices[zone_idx].num_leds;
if(!(zones[zone_idx].flags & ZONE_FLAG_MANUALLY_CONFIGURED_TYPE))
{
zones[zone_idx].type = ZONE_TYPE_LINEAR;
}
if(!(zones[zone_idx].flags & ZONE_FLAG_MANUALLY_CONFIGURED_MATRIX_MAP))
{
zones[zone_idx].matrix_map.width = 0;
zones[zone_idx].matrix_map.height = 0;
zones[zone_idx].matrix_map.map.resize(0);
}
zone led_zone;
led_zone.name = devices[zone_idx].name;
led_zone.type = ZONE_TYPE_LINEAR;
led_zone.leds_min = devices[zone_idx].num_leds;
led_zone.leds_max = devices[zone_idx].num_leds;
led_zone.leds_count = devices[zone_idx].num_leds;
zones.push_back(led_zone);
}
for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++)
@@ -117,22 +75,14 @@ void RGBController_DDP::SetupZones()
for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++)
{
led new_led;
new_led.name = zones[zone_idx].name + " LED " + std::to_string(led_idx + 1);
new_led.name = zones[zone_idx].name + " LED " + std::to_string(led_idx + 1);
new_led.value = 0;
leds.push_back(new_led);
}
}
SetupColors();
}
void RGBController_DDP::DeviceConfigureZone(int zone_idx)
{
if((size_t)zone_idx < zones.size())
{
SetupZones();
}
}
void RGBController_DDP::DeviceUpdateLEDs()
{
std::vector<unsigned int> brightness_adjusted_colors;
@@ -21,8 +21,6 @@ public:
void SetupZones();
void DeviceConfigureZone(int zone_idx);
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
@@ -40,4 +40,8 @@ DetectedControllers DetectDygmaRaiseControllers()
}
REGISTER_DETECTOR("Dygma Raise", DetectDygmaRaiseControllers);
REGISTER_CUSTOM_UDEV_RULE(dygma_raise, "Dygma Raise", "SUBSYSTEMS==\"serial|hidraw\", ATTRS{idVendor}==\"1209\", ATTRS{idProduct}==\"2201\", TAG+=\"uaccess\", TAG+=\"Dygma_Raise\"");
/*---------------------------------------------------------------------------------------------------------*\
| Entries for dynamic UDEV rules |
| |
| DUMMY_DEVICE_DETECTOR("Dygma Raise", DetectDygmaRaiseControllers, 0x1209, 0x2201 ) |
\*---------------------------------------------------------------------------------------------------------*/
@@ -223,68 +223,33 @@ RGBController_E131::~RGBController_E131()
void RGBController_E131::SetupZones()
{
/*-----------------------------------------------------*\
| Only set LED count on the first run |
| Add Zones |
\*-----------------------------------------------------*/
bool first_run = false;
if(zones.size() == 0)
for(std::size_t zone_idx = 0; zone_idx < devices.size(); zone_idx++)
{
first_run = true;
zone led_zone;
led_zone.name = devices[zone_idx].name;
led_zone.type = ZONE_TYPE_LINEAR;
led_zone.leds_min = devices[zone_idx].num_leds;
led_zone.leds_max = devices[zone_idx].num_leds;
led_zone.leds_count = devices[zone_idx].num_leds;
led_zone.flags = ZONE_FLAG_MANUALLY_CONFIGURABLE_TYPE
| ZONE_FLAG_MANUALLY_CONFIGURABLE_MATRIX_MAP
| ZONE_FLAG_MANUALLY_CONFIGURABLE_SEGMENTS;
zones.push_back(led_zone);
}
/*-----------------------------------------------------*\
| Clear any existing color/LED configuration |
| Add LEDs |
\*-----------------------------------------------------*/
leds.clear();
colors.clear();
zones.resize(devices.size());
/*-----------------------------------------------------*\
| Set zones and leds |
\*-----------------------------------------------------*/
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
zones[zone_idx].leds_min = devices[zone_idx].num_leds;
zones[zone_idx].leds_max = devices[zone_idx].num_leds;
if(first_run)
{
zones[zone_idx].flags = ZONE_FLAG_MANUALLY_CONFIGURABLE_NAME
| ZONE_FLAG_MANUALLY_CONFIGURABLE_TYPE
| ZONE_FLAG_MANUALLY_CONFIGURABLE_MATRIX_MAP
| ZONE_FLAG_MANUALLY_CONFIGURABLE_SEGMENTS;
}
if(!(zones[zone_idx].flags & ZONE_FLAG_MANUALLY_CONFIGURED_NAME))
{
zones[zone_idx].name = devices[zone_idx].name;
}
zones[zone_idx].leds_count = devices[zone_idx].num_leds;
if(!(zones[zone_idx].flags & ZONE_FLAG_MANUALLY_CONFIGURED_TYPE))
{
zones[zone_idx].type = ZONE_TYPE_LINEAR;
}
if(!(zones[zone_idx].flags & ZONE_FLAG_MANUALLY_CONFIGURED_MATRIX_MAP))
{
zones[zone_idx].matrix_map.width = 0;
zones[zone_idx].matrix_map.height = 0;
zones[zone_idx].matrix_map.map.resize(0);
}
}
/*-----------------------------------------------------*\
| Add LEDs |
\*-----------------------------------------------------*/
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++)
{
led new_led;
new_led.name = zones[zone_idx].name + " LED ";
new_led.name = zones[zone_idx].name + " LED ";
new_led.name.append(std::to_string(led_idx));
leds.push_back(new_led);
@@ -294,14 +259,6 @@ void RGBController_E131::SetupZones()
SetupColors();
}
void RGBController_E131::DeviceConfigureZone(int zone_idx)
{
if((size_t)zone_idx < zones.size())
{
SetupZones();
}
}
void RGBController_E131::DeviceUpdateLEDs()
{
int color_idx = 0;
@@ -35,8 +35,6 @@ public:
void SetupZones();
void DeviceConfigureZone(int zone_idx);
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
@@ -411,7 +411,6 @@ REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White OC",
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_O24G_GAMING_WHITE_2, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG MATRIX PLATINUM GeForce RTX 4090", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_MATRIX_PLATINUM_RTX_4090_24G, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5060 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5060_O8G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5060 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5060TI_O16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070_O12G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming BTF White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING_BTF_WHITE, 0x67);
@@ -427,7 +426,6 @@ REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 WHITE",
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_2, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC BTF", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_BTF, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC BTF", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_BTF_2, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_WHITE, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL LC GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_LC_RTX_5090_O32G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL LC OC GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_LC_OC_RTX_5090_O32G_GAMING, 0x67);
@@ -1,268 +0,0 @@
/*---------------------------------------------------------*\
| EVGAX12Controller.cpp |
| |
| Driver for the EVGA X12 gaming mouse |
| |
| QuickMythril 08 Aug 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <chrono>
#include <cstring>
#include <thread>
#include "EVGAX12Controller.h"
#include "LogManager.h"
#include "StringUtils.h"
#define EVGA_X12_HEADER 0xEA
#define EVGA_X12_DIRECTION_GET 0x01
#define EVGA_X12_DIRECTION_SET 0x02
#define EVGA_X12_COMMAND_MODE 0x01
#define EVGA_X12_COMMAND_STATIC 0x02
#define EVGA_X12_START_WITH_PROFILE 0x01
#define EVGA_X12_READ_ATTEMPTS 5
#define EVGA_X12_READ_DELAY_MS 5
#define EVGA_X12_WRITE_ATTEMPTS 3
static const unsigned int EVGA_X12_LED_TO_FIRMWARE_SLOT[EVGA_X12_LED_COUNT] =
{
EVGA_X12_FIRMWARE_SLOT_HEAD_LEFT,
EVGA_X12_FIRMWARE_SLOT_WHEEL,
EVGA_X12_FIRMWARE_SLOT_LOGO,
};
EVGAX12Controller::EVGAX12Controller(hid_device* dev_handle, const hid_device_info& info, const std::string& dev_name)
{
dev = dev_handle;
location = info.path;
name = dev_name;
}
EVGAX12Controller::~EVGAX12Controller()
{
hid_close(dev);
}
std::string EVGAX12Controller::GetDeviceLocation()
{
return("HID: " + location);
}
std::string EVGAX12Controller::GetNameString()
{
return(name);
}
std::string EVGAX12Controller::GetSerialString()
{
wchar_t serial_string[128];
int ret = hid_get_serial_number_string(dev, serial_string, 128);
if(ret != 0)
{
return("");
}
return(StringUtils::wstring_to_string(serial_string));
}
EVGAX12LightingState EVGAX12Controller::GetLightingState()
{
std::lock_guard<std::mutex> lock(device_mutex);
EVGAX12LightingState state = {};
unsigned char packet[EVGA_X12_REPORT_SIZE];
state.modes_valid = GetReport(EVGA_X12_COMMAND_MODE, packet);
if(state.modes_valid)
{
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
{
unsigned int firmware_slot = EVGA_X12_LED_TO_FIRMWARE_SLOT[led_idx];
state.modes[led_idx] = packet[4 + firmware_slot];
}
}
state.static_valid = GetReport(EVGA_X12_COMMAND_STATIC, packet);
if(state.static_valid)
{
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
{
unsigned int firmware_slot = EVGA_X12_LED_TO_FIRMWARE_SLOT[led_idx];
unsigned int offset = 4 + (firmware_slot * 4);
state.brightness[led_idx] = packet[offset];
state.colors[led_idx] = ToRGBColor(packet[offset + 1], packet[offset + 2], packet[offset + 3]);
}
}
return(state);
}
bool EVGAX12Controller::SetMode(unsigned char mode)
{
std::lock_guard<std::mutex> lock(device_mutex);
return(SetModeReport(mode));
}
bool EVGAX12Controller::SetModeReport(unsigned char mode)
{
unsigned char packet[EVGA_X12_REPORT_SIZE] = {};
packet[0] = EVGA_X12_REPORT_ID;
packet[1] = EVGA_X12_HEADER;
packet[2] = EVGA_X12_DIRECTION_SET;
packet[3] = EVGA_X12_COMMAND_MODE;
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
{
unsigned int firmware_slot = EVGA_X12_LED_TO_FIRMWARE_SLOT[led_idx];
packet[4 + firmware_slot] = mode;
}
return(SendReport(packet));
}
bool EVGAX12Controller::SetStatic(const std::vector<RGBColor>& colors, unsigned char brightness)
{
if(colors.size() < EVGA_X12_LED_COUNT)
{
LOG_ERROR("[%s] Cannot set static lighting with only %u colors", name.c_str(), (unsigned int)colors.size());
return(false);
}
std::lock_guard<std::mutex> lock(device_mutex);
return(SetStaticReport(colors, brightness));
}
bool EVGAX12Controller::SetStaticAndActivate(const std::vector<RGBColor>& colors, unsigned char brightness)
{
if(colors.size() < EVGA_X12_LED_COUNT)
{
LOG_ERROR("[%s] Cannot set static lighting with only %u colors", name.c_str(), (unsigned int)colors.size());
return(false);
}
std::lock_guard<std::mutex> lock(device_mutex);
if(!SetStaticReport(colors, brightness))
{
return(false);
}
std::this_thread::sleep_for(std::chrono::milliseconds(EVGA_X12_READ_DELAY_MS));
return(SetModeReport(EVGA_X12_MODE_STATIC));
}
bool EVGAX12Controller::SetStaticReport(const std::vector<RGBColor>& colors, unsigned char brightness)
{
unsigned char packet[EVGA_X12_REPORT_SIZE] = {};
packet[0] = EVGA_X12_REPORT_ID;
packet[1] = EVGA_X12_HEADER;
packet[2] = EVGA_X12_DIRECTION_SET;
packet[3] = EVGA_X12_COMMAND_STATIC;
for(unsigned int firmware_slot = 0; firmware_slot < EVGA_X12_FIRMWARE_SLOT_COUNT; firmware_slot++)
{
packet[20 + firmware_slot] = EVGA_X12_START_WITH_PROFILE;
}
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
{
unsigned int firmware_slot = EVGA_X12_LED_TO_FIRMWARE_SLOT[led_idx];
unsigned int offset = 4 + (firmware_slot * 4);
packet[offset] = brightness;
packet[offset + 1] = RGBGetRValue(colors[led_idx]);
packet[offset + 2] = RGBGetGValue(colors[led_idx]);
packet[offset + 3] = RGBGetBValue(colors[led_idx]);
}
for(unsigned int attempt = 0; attempt < EVGA_X12_WRITE_ATTEMPTS; attempt++)
{
if(!SendReport(packet))
{
return(false);
}
std::this_thread::sleep_for(std::chrono::milliseconds(EVGA_X12_READ_DELAY_MS));
unsigned char reply[EVGA_X12_REPORT_SIZE];
if(GetReport(EVGA_X12_COMMAND_STATIC, reply))
{
bool matches = true;
for(unsigned int firmware_slot = 0; firmware_slot < EVGA_X12_FIRMWARE_SLOT_COUNT; firmware_slot++)
{
unsigned int offset = 4 + (firmware_slot * 4);
if(std::memcmp(&packet[offset], &reply[offset], 4) != 0)
{
matches = false;
break;
}
}
if(matches)
{
return(true);
}
}
}
LOG_VERBOSE("[%s] Retries exhausted setting static lighting", name.c_str());
return(false);
}
bool EVGAX12Controller::GetReport(unsigned char command, unsigned char* packet)
{
std::memset(packet, 0, EVGA_X12_REPORT_SIZE);
packet[0] = EVGA_X12_REPORT_ID;
packet[1] = EVGA_X12_HEADER;
packet[2] = EVGA_X12_DIRECTION_GET;
packet[3] = command;
if(!SendReport(packet))
{
return(false);
}
for(unsigned int attempt = 0; attempt < EVGA_X12_READ_ATTEMPTS; attempt++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(EVGA_X12_READ_DELAY_MS));
std::memset(packet, 0, EVGA_X12_REPORT_SIZE);
packet[0] = EVGA_X12_REPORT_ID;
int bytes_read = hid_get_feature_report(dev, packet, EVGA_X12_REPORT_SIZE);
if(bytes_read == EVGA_X12_REPORT_SIZE && packet[1] == EVGA_X12_HEADER && packet[3] == command)
{
return(true);
}
if(bytes_read < 0)
{
LOG_DEBUG("[%s] Error reading feature report: %ls", name.c_str(), hid_error(dev));
return(false);
}
}
LOG_VERBOSE("[%s] Retries exhausted reading command 0x%02X", name.c_str(), command);
return(false);
}
bool EVGAX12Controller::SendReport(const unsigned char* packet)
{
int bytes_written = hid_send_feature_report(dev, packet, EVGA_X12_REPORT_SIZE);
if(bytes_written != EVGA_X12_REPORT_SIZE)
{
LOG_DEBUG("[%s] Error writing feature report: %ls", name.c_str(), hid_error(dev));
return(false);
}
return(true);
}
@@ -1,80 +0,0 @@
/*---------------------------------------------------------*\
| EVGAX12Controller.h |
| |
| Driver for the EVGA X12 gaming mouse |
| |
| QuickMythril 08 Aug 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <array>
#include <mutex>
#include <string>
#include <vector>
#include <hidapi.h>
#include "RGBController.h"
#define EVGA_X12_REPORT_ID 0x07
#define EVGA_X12_REPORT_SIZE 40
#define EVGA_X12_LED_COUNT 3
#define EVGA_X12_FIRMWARE_SLOT_COUNT 4
#define EVGA_X12_BRIGHTNESS_MIN 0
#define EVGA_X12_BRIGHTNESS_MAX 255
enum
{
EVGA_X12_MODE_OFF = 0,
EVGA_X12_MODE_STATIC = 1,
};
/*---------------------------------------------------------*\
| HEAD_LEFT drives both physical side lights. HEAD_RIGHT |
| is writable and readable, but has no physical emitter. |
\*---------------------------------------------------------*/
enum
{
EVGA_X12_FIRMWARE_SLOT_HEAD_LEFT = 0,
EVGA_X12_FIRMWARE_SLOT_HEAD_RIGHT = 1,
EVGA_X12_FIRMWARE_SLOT_WHEEL = 2,
EVGA_X12_FIRMWARE_SLOT_LOGO = 3,
};
struct EVGAX12LightingState
{
std::array<unsigned char, EVGA_X12_LED_COUNT> modes;
std::array<unsigned char, EVGA_X12_LED_COUNT> brightness;
std::array<RGBColor, EVGA_X12_LED_COUNT> colors;
bool modes_valid;
bool static_valid;
};
class EVGAX12Controller
{
public:
EVGAX12Controller(hid_device* dev_handle, const hid_device_info& info, const std::string& dev_name);
~EVGAX12Controller();
std::string GetDeviceLocation();
std::string GetNameString();
std::string GetSerialString();
EVGAX12LightingState GetLightingState();
bool SetMode(unsigned char mode);
bool SetStatic(const std::vector<RGBColor>& colors, unsigned char brightness);
bool SetStaticAndActivate(const std::vector<RGBColor>& colors, unsigned char brightness);
private:
bool GetReport(unsigned char command, unsigned char* packet);
bool SetModeReport(unsigned char mode);
bool SetStaticReport(const std::vector<RGBColor>& colors, unsigned char brightness);
bool SendReport(const unsigned char* packet);
hid_device* dev;
std::string location;
std::string name;
std::mutex device_mutex;
};
@@ -1,40 +0,0 @@
/*---------------------------------------------------------*\
| EVGAX12ControllerDetect.cpp |
| |
| Detector for the EVGA X12 gaming mouse |
| |
| QuickMythril 08 Aug 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "DetectionManager.h"
#include "EVGAX12Controller.h"
#include "RGBController_EVGAX12.h"
#define EVGA_USB_VID 0x3842
#define EVGA_X12_PID 0x2422
#define EVGA_X12_INTERFACE 1
#define EVGA_X12_USAGE_PAGE 0x0008
#define EVGA_X12_USAGE 0x004B
DetectedControllers DetectEVGAX12(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
EVGAX12Controller* controller = new EVGAX12Controller(dev, *info, name);
RGBController_EVGAX12* rgb_controller = new RGBController_EVGAX12(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("EVGA X12 Gaming Mouse", DetectEVGAX12, EVGA_USB_VID, EVGA_X12_PID, EVGA_X12_INTERFACE, EVGA_X12_USAGE_PAGE, EVGA_X12_USAGE);
@@ -1,150 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_EVGAX12.cpp |
| |
| RGBController for the EVGA X12 gaming mouse |
| |
| QuickMythril 08 Aug 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_EVGAX12.h"
/**------------------------------------------------------------------*\
@name EVGA X12 Gaming Mouse
@category Mouse
@type USB
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectEVGAX12
@comment Lighting changes are volatile and revert to the onboard
profile when the mouse is reconnected.
\*-------------------------------------------------------------------*/
RGBController_EVGAX12::RGBController_EVGAX12(EVGAX12Controller* controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "EVGA";
type = DEVICE_TYPE_MOUSE;
description = "EVGA X12 Gaming Mouse";
serial = controller->GetSerialString();
location = controller->GetDeviceLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = EVGA_X12_MODE_STATIC;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.brightness_min = EVGA_X12_BRIGHTNESS_MIN;
Direct.brightness_max = EVGA_X12_BRIGHTNESS_MAX;
Direct.brightness = EVGA_X12_BRIGHTNESS_MAX;
Direct.color_mode = MODE_COLORS_PER_LED;
direct_mode_index = (unsigned int)modes.size();
modes.push_back(Direct);
mode Off;
Off.name = "Off";
Off.value = EVGA_X12_MODE_OFF;
Off.color_mode = MODE_COLORS_NONE;
off_mode_index = (unsigned int)modes.size();
modes.push_back(Off);
active_mode = direct_mode_index;
SetupZones();
EVGAX12LightingState current_state = controller->GetLightingState();
if(current_state.static_valid)
{
modes[direct_mode_index].brightness = current_state.brightness[0];
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
{
colors[led_idx] = current_state.colors[led_idx];
}
}
if(current_state.modes_valid)
{
bool all_off = true;
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
{
if(current_state.modes[led_idx] != EVGA_X12_MODE_OFF)
{
all_off = false;
break;
}
}
if(all_off)
{
active_mode = off_mode_index;
}
}
}
RGBController_EVGAX12::~RGBController_EVGAX12()
{
Shutdown();
delete controller;
}
void RGBController_EVGAX12::SetupZones()
{
const char* zone_names[EVGA_X12_LED_COUNT] =
{
"Side",
"Scroll Wheel",
"Logo",
};
for(unsigned int led_idx = 0; led_idx < EVGA_X12_LED_COUNT; led_idx++)
{
zone new_zone;
new_zone.name = zone_names[led_idx];
new_zone.type = ZONE_TYPE_SINGLE;
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.name = std::string(zone_names[led_idx]) + " LED";
new_led.value = led_idx;
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_EVGAX12::DeviceUpdateLEDs()
{
controller->SetStatic(colors, modes[direct_mode_index].brightness);
}
void RGBController_EVGAX12::DeviceUpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_EVGAX12::DeviceUpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_EVGAX12::DeviceUpdateMode()
{
if(modes[active_mode].value == EVGA_X12_MODE_OFF)
{
controller->SetMode(EVGA_X12_MODE_OFF);
}
else
{
controller->SetStaticAndActivate(colors, modes[direct_mode_index].brightness);
}
}
@@ -1,34 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_EVGAX12.h |
| |
| RGBController for the EVGA X12 gaming mouse |
| |
| QuickMythril 08 Aug 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "EVGAX12Controller.h"
#include "RGBController.h"
class RGBController_EVGAX12 : public RGBController
{
public:
RGBController_EVGAX12(EVGAX12Controller* controller_ptr);
~RGBController_EVGAX12();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
private:
void SetupZones();
EVGAX12Controller* controller;
unsigned int direct_mode_index;
unsigned int off_mode_index;
};
@@ -1,205 +0,0 @@
/*---------------------------------------------------------*\
| EdifierHaloController.cpp |
| |
| Driver for Edifier Halo series USB/HID devices |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include "LogManager.h"
#include "EdifierHaloController.h"
#include "StringUtils.h"
EdifierHaloController::EdifierHaloController(hid_device* dev_handle, const char* path)
{
dev = dev_handle;
location = path;
wchar_t name_string[256];
int result;
result = hid_get_manufacturer_string(dev, name_string, 256);
if(result == 0)
{
device_name = StringUtils::wstring_to_string(name_string);
}
else
{
device_name = "Edifier";
LOG_WARNING("[EdifierHaloController] Failed to read manufacturer string for %s", path);
}
result = hid_get_product_string(dev, name_string, 256);
if(result == 0)
{
device_name.append(" ").append(StringUtils::wstring_to_string(name_string));
}
else
{
device_name.append(" Halo PixelBar");
LOG_WARNING("[EdifierHaloController] Failed to read product string for %s", path);
}
wchar_t serial_string[128];
result = hid_get_serial_number_string(dev, serial_string, 128);
if(result == 0)
{
serial = StringUtils::wstring_to_string(serial_string);
}
else
{
serial = "";
LOG_DEBUG("[EdifierHaloController] No serial number available for %s", path);
}
}
EdifierHaloController::~EdifierHaloController()
{
if(dev)
{
hid_close(dev);
}
}
std::string EdifierHaloController::GetDeviceName() const
{
return(device_name);
}
std::string EdifierHaloController::GetSerial() const
{
return(serial);
}
std::string EdifierHaloController::GetLocation() const
{
return("HID: " + location);
}
void EdifierHaloController::SetAmbientLightPower(bool enabled)
{
unsigned char mode = enabled ? 0x01 : 0x00;
unsigned char payload[] =
{
0x00,
0x00,
0x00,
0x00,
mode,
0xFF,
0xFF
};
SendEdifierPacket(0x6b, payload, sizeof(payload));
}
void EdifierHaloController::SetAmbientLight(unsigned char effect, unsigned char r, unsigned char g, unsigned char b, unsigned char brightness, unsigned char speed)
{
unsigned char raw_brightness = 0x3c;
if (brightness == 1) raw_brightness = 0x14;
else if (brightness == 2) raw_brightness = 0x28;
else if (brightness == 3) raw_brightness = 0x3c;
if (speed < 1) speed = 1;
if (speed > 10) speed = 10;
unsigned char payload[] =
{
0x13,
effect,
r,
g,
b,
raw_brightness,
speed
};
SendEdifierPacket(0x6b, payload, sizeof(payload));
}
void EdifierHaloController::SetPixelScreenColor(unsigned char r, unsigned char g, unsigned char b)
{
unsigned char payload[] =
{
0x03,
r,
g,
b
};
SendEdifierPacket(0xef, payload, sizeof(payload));
}
void EdifierHaloController::SetScreenUIModel(unsigned char model_id, unsigned char r, unsigned char g, unsigned char b)
{
unsigned char payload[] =
{
0x02,
r,
g,
b,
0x00,
0x01,
model_id,
0xff,
0xff
};
SendEdifierPacket(0xef, payload, sizeof(payload));
}
void EdifierHaloController::SetScreenScene(unsigned char category, unsigned char index, unsigned char r, unsigned char g, unsigned char b)
{
unsigned char payload[] =
{
0x01, // group
r,
g,
b,
0x00,
0x01,
category,
index,
0xff // option
};
SendEdifierPacket(0xef, payload, sizeof(payload));
}
void EdifierHaloController::SendEdifierPacket(unsigned char commandIndex, const unsigned char* payload, unsigned int payload_size)
{
if(!dev)
{
return;
}
unsigned char packet[64];
memset(packet, 0x00, sizeof(packet));
packet[0] = 0x2e;
packet[1] = 0xaa;
packet[2] = 0xec;
packet[3] = commandIndex;
packet[4] = (unsigned char)((payload_size >> 8) & 0xff);
packet[5] = (unsigned char)(payload_size & 0xff);
if(payload_size > 0 && payload != nullptr)
{
memcpy(&packet[6], payload, payload_size);
}
// Calculate checksum
unsigned int sum = 0;
for(unsigned int i = 1; i < 6 + payload_size; i++)
{
sum += packet[i];
}
packet[6 + payload_size] = (unsigned char)(sum & 0xff);
std::lock_guard<std::mutex> guard(device_mutex);
int bytes_written = hid_write(dev, packet, sizeof(packet));
if(bytes_written != sizeof(packet))
{
LOG_ERROR("[EdifierHaloController] Failed to write HID packet (%d/%d bytes): %ls",
bytes_written, (int)sizeof(packet), hid_error(dev));
}
}
@@ -1,64 +0,0 @@
/*---------------------------------------------------------*\
| EdifierHaloController.h |
| |
| Driver for Edifier Halo series USB/HID devices |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <mutex>
#include <string>
#include <hidapi.h>
enum
{
EDIFIER_HALO_AMBIENT_MODE_BREATHING = 1, // Breathing
EDIFIER_HALO_AMBIENT_MODE_TIDE = 2, // Rainbow
EDIFIER_HALO_AMBIENT_MODE_STATIC = 3, // Static
EDIFIER_HALO_AMBIENT_MODE_RIPPLE = 4, // Glittering and colorful
EDIFIER_HALO_AMBIENT_MODE_FLOW = 5, // Light-flow
EDIFIER_HALO_AMBIENT_MODE_DYNAMIC = 6 // Dynamic Shadows
};
enum
{
EDIFIER_HALO_AMBIENT_SPEED_SLOWEST = 1,
EDIFIER_HALO_AMBIENT_SPEED_FASTEST = 10
};
enum
{
EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST = 1,
EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST = 3
};
class EdifierHaloController
{
public:
EdifierHaloController(hid_device* dev_handle, const char* path);
~EdifierHaloController();
std::string GetDeviceName() const;
std::string GetSerial() const;
std::string GetLocation() const;
void SetAmbientLight(unsigned char effect, unsigned char r, unsigned char g, unsigned char b, unsigned char brightness, unsigned char speed);
void SetAmbientLightPower(bool enabled);
void SetPixelScreenColor(unsigned char r, unsigned char g, unsigned char b);
void SetScreenUIModel(unsigned char model_id, unsigned char r, unsigned char g, unsigned char b);
void SetScreenScene(unsigned char category, unsigned char index, unsigned char r, unsigned char g, unsigned char b);
private:
hid_device* dev;
std::string device_name;
std::string serial;
std::string location;
std::mutex device_mutex;
void SendEdifierPacket(unsigned char commandIndex, const unsigned char* payload, unsigned int payload_size);
};
@@ -1,56 +0,0 @@
/*---------------------------------------------------------*\
| EdifierHaloControllerDetect.cpp |
| |
| Detector for Edifier Halo PixelBar USB/HID devices |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include <memory>
#include "DetectionManager.h"
#include "LogManager.h"
#include "RGBController_EdifierHaloPixelbarAmbient.h"
#include "RGBController_EdifierHaloPixelbarScreen.h"
#include "EdifierHaloController.h"
/*---------------------------------------------------------*\
| Edifier HID vendor and product IDs |
\*---------------------------------------------------------*/
#define EDIFIER_VID 0x2D99
#define EDIFIER_PIXELBAR_PID 0xA106
#define EDIFIER_PIXELBAR_INTERFACE 4
#define EDIFIER_PIXELBAR_USAGE_PAGE 0xFF14
#define EDIFIER_PIXELBAR_USAGE 0x01
DetectedControllers DetectEdifierHaloControllers(hid_device_info* info, const std::string&)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
EdifierHaloController* controller = new EdifierHaloController(dev, info->path);
RGBController_EdifierHaloPixelbarAmbient* ambient_controller = new RGBController_EdifierHaloPixelbarAmbient(controller);
RGBController_EdifierHaloPixelbarScreen* screen_controller = new RGBController_EdifierHaloPixelbarScreen(controller);
detected_controllers.push_back(ambient_controller);
detected_controllers.push_back(screen_controller);
LOG_INFO("[EdifierHaloPixelbarDetect] Detected Edifier Halo PixelBar at %s (Instantiated Ambient & Screen controllers)", info->path);
}
else
{
LOG_WARNING("[EdifierHaloPixelbarDetect] Failed to open Edifier Halo PixelBar at %s", info->path);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Edifier Halo Pixelbar", DetectEdifierHaloControllers, EDIFIER_VID, EDIFIER_PIXELBAR_PID, EDIFIER_PIXELBAR_INTERFACE, EDIFIER_PIXELBAR_USAGE_PAGE, EDIFIER_PIXELBAR_USAGE);
@@ -1,182 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_EdifierHaloPixelbarAmbient.cpp |
| |
| RGBController for Edifier Halo PixelBar Ambient Light |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_EdifierHaloPixelbarAmbient.h"
/**------------------------------------------------------------------*\
@name Edifier Halo Pixelbar
@category Speaker
@type USB
@save :robot:
@direct :x:
@effects :white_check_mark:
@detectors DetectEdifierHaloControllers
@comment
\*-------------------------------------------------------------------*/
RGBController_EdifierHaloPixelbarAmbient::RGBController_EdifierHaloPixelbarAmbient(EdifierHaloController* controller_ptr)
{
controller = controller_ptr;
first_leds_update_ignored = false;
first_mode_update_ignored = false;
name = "Edifier Halo Pixelbar - Light Effects";
vendor = "Edifier";
type = DEVICE_TYPE_SPEAKER;
description = "Edifier Halo PixelBar Light Effects";
serial = controller->GetSerial();
location = controller->GetLocation();
// Setup modes
mode breathing_mode;
breathing_mode.name = "Breathing";
breathing_mode.value = EDIFIER_HALO_AMBIENT_MODE_BREATHING;
breathing_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
breathing_mode.color_mode = MODE_COLORS_PER_LED;
breathing_mode.speed_min = EDIFIER_HALO_AMBIENT_SPEED_SLOWEST;
breathing_mode.speed_max = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
breathing_mode.speed = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
breathing_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
breathing_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
breathing_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(breathing_mode);
mode tide_mode;
tide_mode.name = "Rainbow";
tide_mode.value = EDIFIER_HALO_AMBIENT_MODE_TIDE;
tide_mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
tide_mode.color_mode = MODE_COLORS_NONE;
tide_mode.speed_min = EDIFIER_HALO_AMBIENT_SPEED_SLOWEST;
tide_mode.speed_max = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
tide_mode.speed = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
tide_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
tide_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
tide_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(tide_mode);
mode static_mode;
static_mode.name = "Static";
static_mode.value = EDIFIER_HALO_AMBIENT_MODE_STATIC;
static_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
static_mode.color_mode = MODE_COLORS_MODE_SPECIFIC;
static_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
static_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
static_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(static_mode);
mode ripple_mode;
ripple_mode.name = "Glittering and colorful";
ripple_mode.value = EDIFIER_HALO_AMBIENT_MODE_RIPPLE;
ripple_mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
ripple_mode.color_mode = MODE_COLORS_NONE;
ripple_mode.speed_min = EDIFIER_HALO_AMBIENT_SPEED_SLOWEST;
ripple_mode.speed_max = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
ripple_mode.speed = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
ripple_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
ripple_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
ripple_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(ripple_mode);
mode flow_mode;
flow_mode.name = "Light-flow";
flow_mode.value = EDIFIER_HALO_AMBIENT_MODE_FLOW;
flow_mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
flow_mode.color_mode = MODE_COLORS_NONE;
flow_mode.speed_min = EDIFIER_HALO_AMBIENT_SPEED_SLOWEST;
flow_mode.speed_max = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
flow_mode.speed = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
flow_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
flow_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
flow_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(flow_mode);
mode dynamic_mode;
dynamic_mode.name = "Dynamic Shadows";
dynamic_mode.value = EDIFIER_HALO_AMBIENT_MODE_DYNAMIC;
dynamic_mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
dynamic_mode.color_mode = MODE_COLORS_NONE;
dynamic_mode.speed_min = EDIFIER_HALO_AMBIENT_SPEED_SLOWEST;
dynamic_mode.speed_max = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
dynamic_mode.speed = EDIFIER_HALO_AMBIENT_SPEED_FASTEST;
dynamic_mode.brightness_min = EDIFIER_HALO_AMBIENT_BRIGHTNESS_LOWEST;
dynamic_mode.brightness_max = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
dynamic_mode.brightness = EDIFIER_HALO_AMBIENT_BRIGHTNESS_HIGHEST;
modes.push_back(dynamic_mode);
// Default to Static mode
active_mode = 2;
SetupZones();
}
RGBController_EdifierHaloPixelbarAmbient::~RGBController_EdifierHaloPixelbarAmbient()
{
}
void RGBController_EdifierHaloPixelbarAmbient::SetupZones()
{
zone ambient_zone;
ambient_zone.name = "Light Effects";
ambient_zone.type = ZONE_TYPE_SINGLE;
ambient_zone.leds_min = 1;
ambient_zone.leds_max = 1;
ambient_zone.leds_count = 1;
zones.push_back(ambient_zone);
led ambient_led;
ambient_led.name = "Light Effects";
leds.push_back(ambient_led);
SetupColors();
}
void RGBController_EdifierHaloPixelbarAmbient::DeviceUpdateLEDs()
{
if(!first_leds_update_ignored)
{
first_leds_update_ignored = true;
return;
}
int active_idx = GetActiveMode();
RGBColor color = colors[0];
unsigned char r = RGBGetRValue(color);
unsigned char g = RGBGetGValue(color);
unsigned char b = RGBGetBValue(color);
unsigned char effect = modes[active_idx].value;
unsigned char brightness = modes[active_idx].brightness;
unsigned char speed = modes[active_idx].speed;
controller->SetAmbientLightPower(true);
controller->SetAmbientLight(effect, r, g, b, brightness, speed);
}
void RGBController_EdifierHaloPixelbarAmbient::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_EdifierHaloPixelbarAmbient::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_EdifierHaloPixelbarAmbient::DeviceUpdateMode()
{
if(!first_mode_update_ignored)
{
first_mode_update_ignored = true;
return;
}
DeviceUpdateLEDs();
}
@@ -1,35 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_EdifierHaloPixelbarAmbient.h |
| |
| RGBController for Edifier Halo PixelBar Ambient Light |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <memory>
#include "RGBController.h"
#include "EdifierHaloController.h"
class RGBController_EdifierHaloPixelbarAmbient : public RGBController
{
public:
RGBController_EdifierHaloPixelbarAmbient(EdifierHaloController* controller_ptr);
~RGBController_EdifierHaloPixelbarAmbient();
void SetupZones();
void DeviceUpdateLEDs();
void DeviceUpdateZoneLEDs(int zone);
void DeviceUpdateSingleLED(int led);
void DeviceUpdateMode();
private:
EdifierHaloController* controller;
bool first_leds_update_ignored;
bool first_mode_update_ignored;
};
@@ -1,173 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_EdifierHaloPixelbarScreen.cpp |
| |
| RGBController for Edifier Halo PixelBar Screen Color |
| |
| Bartholomew Ho <[email protected]> 20 Jul 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_EdifierHaloPixelbarScreen.h"
/**------------------------------------------------------------------*\
@name Edifier Halo Pixelbar - Screen
@category Speaker
@type USB
@save :robot:
@direct :x:
@effects :white_check_mark:
@detectors DetectEdifierHaloControllers
@comment
\*-------------------------------------------------------------------*/
struct CategoryDefinition
{
std::string name;
unsigned char category_id;
};
RGBController_EdifierHaloPixelbarScreen::RGBController_EdifierHaloPixelbarScreen(EdifierHaloController* controller_ptr)
{
controller = controller_ptr;
first_leds_update_ignored = false;
first_mode_update_ignored = false;
name = "Edifier Halo Pixelbar - Pixel Display";
vendor = "Edifier";
type = DEVICE_TYPE_SPEAKER;
description = "Edifier Halo PixelBar Pixel Display Color";
serial = controller->GetSerial();
location = controller->GetLocation();
// Clock 1 to Clock 11 (11 built-in clock styles)
for(unsigned char i = 0; i < 11; ++i)
{
mode m;
m.name = "Clock " + std::to_string(i + 1);
m.value = (0 << 8) | i;
m.flags = MODE_FLAG_HAS_PER_LED_COLOR;
m.color_mode = MODE_COLORS_PER_LED;
modes.push_back(m);
}
// Default built-in layouts for other categories
std::vector<CategoryDefinition> categories =
{
{ "Gaming", 1 },
{ "Work", 2 },
{ "Study", 3 },
{ "Cats", 4 },
{ "Dogs", 5 },
{ "Buzzwords", 6 },
{ "Cyberpunk", 7 },
{ "Spectrum", 8 }
};
for(const CategoryDefinition& cat : categories)
{
mode m;
m.name = cat.name;
m.value = (cat.category_id << 8) | 0;
m.flags = MODE_FLAG_HAS_PER_LED_COLOR;
m.color_mode = MODE_COLORS_PER_LED;
modes.push_back(m);
}
// Default to Clock 1 mode
active_mode = 0;
SetupZones();
}
RGBController_EdifierHaloPixelbarScreen::~RGBController_EdifierHaloPixelbarScreen()
{
delete controller;
}
void RGBController_EdifierHaloPixelbarScreen::SetupZones()
{
zone screen_zone;
screen_zone.name = "Screen Color";
screen_zone.type = ZONE_TYPE_SINGLE;
screen_zone.leds_min = 1;
screen_zone.leds_max = 1;
screen_zone.leds_count = 1;
zones.push_back(screen_zone);
led screen_led;
screen_led.name = "Screen Text";
leds.push_back(screen_led);
SetupColors();
}
void RGBController_EdifierHaloPixelbarScreen::DeviceUpdateLEDs()
{
if(!first_leds_update_ignored)
{
first_leds_update_ignored = true;
return;
}
unsigned char r = 255;
unsigned char g = 255;
unsigned char b = 255;
if(!colors.empty())
{
RGBColor color = colors[0];
r = RGBGetRValue(color);
g = RGBGetGValue(color);
b = RGBGetBValue(color);
}
// For all other modes, color updates are sent using SetPixelScreenColor
controller->SetPixelScreenColor(r, g, b);
}
void RGBController_EdifierHaloPixelbarScreen::DeviceUpdateZoneLEDs(int)
{
DeviceUpdateLEDs();
}
void RGBController_EdifierHaloPixelbarScreen::DeviceUpdateSingleLED(int)
{
DeviceUpdateLEDs();
}
void RGBController_EdifierHaloPixelbarScreen::DeviceUpdateMode()
{
if(!first_mode_update_ignored)
{
first_mode_update_ignored = true;
return;
}
int active_idx = GetActiveMode();
unsigned char r = 255;
unsigned char g = 255;
unsigned char b = 255;
if(!colors.empty())
{
RGBColor color = colors[0];
r = RGBGetRValue(color);
g = RGBGetGValue(color);
b = RGBGetBValue(color);
}
unsigned int mode_val = modes[active_idx].value;
unsigned char category = (mode_val >> 8) & 0xFF;
unsigned char index = mode_val & 0xFF;
if(category == 0) // Clock category (11 built-in styles)
{
controller->SetScreenScene(0, index, r, g, b);
}
else // Other categories (Gaming, Working, etc.) use the default built-in layouts
{
controller->SetScreenUIModel(category, r, g, b);
}
}

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