Publish LumaOps source

This commit is contained in:
LumaOps release export
2026-09-03 01:18:36 +02:00
commit 7f1c0e5f71
2363 changed files with 501543 additions and 0 deletions
@@ -0,0 +1,160 @@
/*---------------------------------------------------------*\
| RGBController_SteelSeriesApex.cpp |
| |
| RGBController for SteelSeries Apex 7 |
| |
| Eric Samuelson (edbgon) 05 Jul 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBControllerKeyNames.h"
#include "RGBController_SteelSeriesApex.h"
#include "SteelSeriesApexRegions.h"
static const char* zone_names[] =
{
ZONE_EN_KEYBOARD
};
static zone_type zone_types[] =
{
ZONE_TYPE_MATRIX
};
static const unsigned int zone_sizes[] =
{
sizeof(led_names)/sizeof(char*),
};
/**------------------------------------------------------------------*\
@name Steel Series APEX
@category Keyboard
@type USB
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectSteelSeriesApex,DetectSteelSeriesApexM
@comment
\*-------------------------------------------------------------------*/
RGBController_SteelSeriesApex::RGBController_SteelSeriesApex(SteelSeriesApexBaseController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetName();
vendor = "SteelSeries";
type = DEVICE_TYPE_KEYBOARD;
description = "SteelSeries Apex RGB Device";
location = controller->GetLocation();
serial = controller->GetSerial();
version = controller->GetVersion();
proto_type = controller->proto_type;
mode Direct;
Direct.name = "Direct";
Direct.value = 0x00;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
}
RGBController_SteelSeriesApex::~RGBController_SteelSeriesApex()
{
/*---------------------------------------------------------*\
| Delete the matrix map |
\*---------------------------------------------------------*/
for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++)
{
if(zones[zone_index].matrix_map != NULL)
{
free(zones[zone_index].matrix_map->map);
delete zones[zone_index].matrix_map;
}
}
delete controller;
}
void RGBController_SteelSeriesApex::SetupZones()
{
/*---------------------------------------------------------*\
| Set up zones |
\*---------------------------------------------------------*/
/*---------------------------------------------------------*\
| The first 5 chars are the SKU which we need to determine |
| the region. |
\*---------------------------------------------------------*/
std::string sku = serial.substr(0, 5);
unsigned int total_led_count = 0;
for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++)
{
zone new_zone;
new_zone.name = zone_names[zone_idx];
new_zone.type = zone_types[zone_idx];
if(zone_types[zone_idx] == ZONE_TYPE_MATRIX)
{
new_zone.matrix_map = new matrix_map_type;
new_zone.matrix_map->map = (unsigned int *) malloc(matrix_mapsize*sizeof(unsigned int));
if((proto_type == APEX) || (proto_type == APEX_M) || (proto_type == APEX_9_TKL) || (proto_type == APEX_9_MINI))
{
SetSkuRegion(*new_zone.matrix_map, sku);
}
}
else
{
new_zone.matrix_map = NULL;
}
if((proto_type == APEX) || (proto_type == APEX_M) || (proto_type == APEX_9_TKL) || (proto_type == APEX_9_MINI))
{
new_zone.leds_min = zone_sizes[zone_idx];
new_zone.leds_max = zone_sizes[zone_idx];
new_zone.leds_count = zone_sizes[zone_idx];
total_led_count += zone_sizes[zone_idx];
}
zones.push_back(new_zone);
};
SetSkuLedNames(leds, sku, total_led_count);
SetupColors();
}
void RGBController_SteelSeriesApex::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_SteelSeriesApex::DeviceUpdateLEDs()
{
last_update_time = std::chrono::steady_clock::now();
controller->SetLEDsDirect(colors);
}
void RGBController_SteelSeriesApex::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_SteelSeriesApex::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_SteelSeriesApex::DeviceUpdateMode()
{
std::vector<RGBColor> temp_colors;
controller->SetMode(modes[active_mode].value, temp_colors);
}
@@ -0,0 +1,39 @@
/*---------------------------------------------------------*\
| RGBController_SteelSeriesApex.h |
| |
| RGBController for SteelSeries Apex 7 |
| |
| Eric Samuelson (edbgon) 05 Jul 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <chrono>
#include "RGBController.h"
#include "SteelSeriesApexBaseController.h"
#include "SteelSeriesGeneric.h"
class RGBController_SteelSeriesApex : public RGBController
{
public:
RGBController_SteelSeriesApex(SteelSeriesApexBaseController* controller_ptr);
~RGBController_SteelSeriesApex();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
SteelSeriesApexBaseController* controller;
steelseries_type proto_type;
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
};
@@ -0,0 +1,318 @@
/*---------------------------------------------------------*\
| SteelSeriesApexController.cpp |
| |
| Driver for SteelSeries Apex Keyboards |
| |
| New driver based on SignalRGB Plugins |
| https://gitlab.com/signalrgb/signal-plugins/ |
| |
| Eric Samuelson (edbgon) 05 Jul 2020 |
| Filipe S. (filipesn) 5 Jan 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include <cstdio>
#include "SteelSeriesApexController.h"
#include "LogManager.h"
using namespace std::chrono_literals;
#define FIRMWARE_REQ_LEN 645
static unsigned int keys[] = {0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, //20
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, //40
0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, //60
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x64, 0xE0, //80
0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xF0, 0x31, 0x87,
0x88, 0x89, 0x8A, 0x8B, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, //100
0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62,
0x63, 0xFB };
SteelSeriesApexController::SteelSeriesApexController(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name) : SteelSeriesApexBaseController(dev_handle, path, dev_name)
{
proto_type = type;
kbd_quirk = APEX_GEN1;
SendInitialization();
}
SteelSeriesApexController::~SteelSeriesApexController()
{
/*-----------------------------------------------------*\
| Gen 3 models must be explicitly cleared for on-board |
| config selection to apply without power cycling after |
| OpenRGB shuts down. |
\*-----------------------------------------------------*/
if(kbd_quirk == APEX_GEN3)
{
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
memset(obuf, 0x00, sizeof(obuf));
obuf[0x00] = 0;
obuf[0x01] = APEX_GEN3_PACKET_CLEAR_LIGHTING;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
}
hid_close(dev);
}
void SteelSeriesApexController::SetMode
(
unsigned char mode,
std::vector<RGBColor> /*colors*/
)
{
unsigned char mode_colors[9];
active_mode = mode;
memset(mode_colors, 0x00, sizeof(mode_colors));
}
void SteelSeriesApexController::SetLEDsDirect(std::vector<RGBColor> colors)
{
unsigned char buf[643];
int num_keys = 0;
unsigned char packet_id = APEX_PACKET_ID_DIRECT;
num_keys = sizeof(keys) / sizeof(*keys);
if(kbd_quirk >= APEX_GEN2)
{
struct hid_device_info* info = hid_get_device_info(dev);
/*-------------------------------------------------*\
| Apparently Gen 3 wireless models reuse this |
| protocol, make sure to place their PID here and |
| further below when developing. |
\*-------------------------------------------------*/
if(info && (info->product_id == 0x1630 || info->product_id == 0x1632
|| info->product_id == 0x162C || info->product_id == 0x162D
|| info->product_id == 0x1644 || info->product_id == 0x1646))
{
packet_id = APEX_2023_PACKET_ID_DIRECT_WIRELESS;
}
else
{
packet_id = APEX_2023_PACKET_ID_DIRECT;
}
}
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
/*-----------------------------------------------------*\
| Set up Direct packet |
\*-----------------------------------------------------*/
buf[0x00] = 0;
buf[0x01] = packet_id;
buf[0x02] = kbd_quirk ? (unsigned char)colors.size() : num_keys;
/*-----------------------------------------------------*\
| Fill in color data |
\*-----------------------------------------------------*/
for(int i = 0; i < num_keys; i++)
{
buf[(i*4)+3] = keys[i];
buf[(i*4)+4] = RGBGetRValue(colors[i]);
buf[(i*4)+5] = RGBGetGValue(colors[i]);
buf[(i*4)+6] = RGBGetBValue(colors[i]);
}
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_send_feature_report(dev, buf, 643);
}
/*---------------------------------------------------------*\
| Private packet sending functions. |
\*---------------------------------------------------------*/
void SteelSeriesApexController::SelectProfile
(
unsigned char profile
)
{
unsigned char buf[65];
/*-----------------------------------------------------*\
| Zero out buffer, set up packet and send |
\*-----------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0;
buf[0x01] = 0x89;
buf[0x02] = profile;
hid_send_feature_report(dev, buf, 65);
}
void SteelSeriesApexController::SendInitialization()
{
unsigned char buf[FIRMWARE_REQ_LEN];
unsigned char read_buf[65];
int res = 0;
char version_str[65] = "Unknown";
struct hid_device_info* info = hid_get_device_info(dev);
unsigned short pid = (info) ? info->product_id : 0;
/*-----------------------------------------------------*\
| Firmware check for TKL 2023 |
\*-----------------------------------------------------*/
if(pid == 0x1628)
{
/*-------------------------------------------------*\
| Zero out buffer |
\*-------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x00;
buf[0x01] = 0x90;
/*-------------------------------------------------*\
| Send packet |
\*-------------------------------------------------*/
hid_write(dev, buf, 65);
/*-------------------------------------------------*\
| Read Response |
\*-------------------------------------------------*/
memset(read_buf, 0x00, sizeof(read_buf));
res = hid_read_timeout(dev, read_buf, sizeof(read_buf), 200);
/*-------------------------------------------------*\
| Firmware Check |
\*-------------------------------------------------*/
if(res > 2 && read_buf[0] == 0x90)
{
int major = 0, minor = 0, patch = 0;
char* fw_ptr = (char*)&read_buf[2];
snprintf(version_str, sizeof(version_str), "%s", fw_ptr);
int count = sscanf(version_str, "%d.%d.%d", &major, &minor, &patch);
if(count == 3)
{
/*-----------------------------------------*\
| Currently set to 1.19.7 or newer. |
\*-----------------------------------------*/
if(major > 1)
{
kbd_quirk = APEX_GEN2;
}
else if(major == 1)
{
if(minor > 19)
{
kbd_quirk = APEX_GEN2;
}
else if(minor == 19 && patch >= 7)
{
kbd_quirk = APEX_GEN2;
}
}
}
}
}
/*-----------------------------------------------------*\
| Apparently Gen 3 models reuse this protocol, make |
| sure to place their PID here and further above for |
| wireless when developing. |
\*-----------------------------------------------------*/
else if(pid == 0x1630 || pid == 0x1632
|| pid == 0x162C || pid == 0x162D
|| pid == 0x1642 || pid == 0x1644 || pid == 0x1646)
{
kbd_quirk = APEX_GEN3;
}
/*-----------------------------------------------------*\
| Send Initialization packet on new protocol. |
\*-----------------------------------------------------*/
if(kbd_quirk >= APEX_GEN2)
{
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x00;
buf[0x01] = APEX_2023_PACKET_ID_INIT;
hid_send_feature_report(dev, buf, APEX_2023_PACKET_LENGTH);
LOG_DEBUG("[%s] Using Apex 2023 protocol. FW: %s", name.c_str(), version_str);
}
else
{
LOG_DEBUG("[%s] Using Apex Legacy protocol. FW: %s", name.c_str(), version_str);
}
}
std::string SteelSeriesApexController::GetSerial()
{
/*-------------------------------------------------*\
| Gen 3 doesn't expose the serial number in |
| firmware. A region code is instead set by the |
| user and subsequently read back. This region code |
| is used by all 5 on-board configs. |
| For consistency with other Apex keyboards, this |
| region code in combination with the PID is mapped |
| to an approximate product number as the region |
| patch logic from that point is identical. |
| The product number used may not be an exact match |
| for the keyboard but should reflect the form |
| factor, region and RGB layout |
\*-------------------------------------------------*/
if(kbd_quirk >= APEX_GEN2)
{
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE];
int result;
struct hid_device_info* info = hid_get_device_info(dev);
unsigned short pid = (info) ? info->product_id : 0;
memset(obuf, 0x00, sizeof(obuf));
if(pid == 0x1642)
{
obuf[0x00] = 0;
obuf[0x01] = 0xF5;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2);
if(result > 3 && ibuf[0] == 0xF5)
{
switch(ibuf[2])
{
case 0x1:
return "64740";
break;
case 0x3:
return "64741";
break;
case 0x4:
return "64743";
break;
case 0x6:
return "64744";
break;
case 0xA:
return "64742";
break;
case 0xD:
return "64745";
break;
default:
break;
}
}
}
return "64865";
}
return SteelSeriesApexBaseController::GetSerial();
}
@@ -0,0 +1,59 @@
/*---------------------------------------------------------*\
| SteelSeriesApexController.cpp |
| |
| Driver for SteelSeries Apex Keyboards |
| |
| New driver based on SignalRGB Plugins |
| https://gitlab.com/signalrgb/signal-plugins/ |
| |
| Eric Samuelson (edbgon) 05 Jul 2020 |
| Filipe S. (filipesn) 5 Jan 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesApexBaseController.h"
enum
{
APEX_PACKET_ID_DIRECT = 0x3a, /* Direct mode */
APEX_2023_PACKET_ID_DIRECT = 0x40, /* New Wired Direct mode */
APEX_2023_PACKET_ID_DIRECT_WIRELESS = 0x61, /* New Wireless Direct mode */
APEX_2023_PACKET_ID_INIT = 0x4B, /* New Initialization */
APEX_2023_PACKET_LENGTH = 643,
APEX_GEN3_PACKET_CLEAR_LIGHTING = 0x41,
};
class SteelSeriesApexController : public SteelSeriesApexBaseController
{
public:
SteelSeriesApexController(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name);
~SteelSeriesApexController();
void SetMode
(
unsigned char mode,
std::vector<RGBColor> colors
);
void SetLEDsDirect(std::vector<RGBColor> colors);
std::string GetSerial() override;
private:
void SelectProfile
(
unsigned char profile
);
void SendInitialization();
};