Add support for SteelSeries Apex 9

master
Patrick Uven 5 months ago committed by Adam Honse
parent 70f3ae1b6d
commit c541716225

@ -0,0 +1,160 @@
/*---------------------------------------------------------*\
| SteelSeriesApex9Controller.cpp |
| |
| Driver for SteelSeries Apex 9 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
#include <cstring>
#include "SteelSeriesApex9Controller.h"
using namespace std::chrono_literals;
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 };
SteelSeriesApex9Controller::SteelSeriesApex9Controller(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name) : SteelSeriesApexBaseController (dev_handle, path, dev_name)
{
proto_type = type;
}
SteelSeriesApex9Controller::~SteelSeriesApex9Controller()
{
hid_close(dev);
}
void SteelSeriesApex9Controller::SetMode(unsigned char mode /*mode*/, std::vector<RGBColor> /*colors*/ )
{
unsigned char mode_colors[9];
active_mode = mode;
memset(mode_colors, 0x00, sizeof(mode_colors));
}
void SteelSeriesApex9Controller::SetLEDsDirect(std::vector<RGBColor> colors)
{
unsigned char buf[APEX_9_PACKET_LENGTH];
int num_keys = 0;
num_keys = sizeof(keys) / sizeof(*keys);
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
/*-----------------------------------------------------*\
| Set up Direct packet |
\*-----------------------------------------------------*/
buf[0x00] = 0;
buf[0x01] = APEX_9_PACKET_ID_DIRECT;
buf[0x02] = 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, APEX_9_PACKET_LENGTH);
}
std::string SteelSeriesApex9Controller::GetSerial()
{
std::string return_string = "";
switch(proto_type)
{
case APEX_9_TKL:
return_string = "64847";
break;
case APEX_9_MINI:
return_string = "64837";
break;
default:
return_string = "Apex 9 GetSerial() error";
}
return(return_string);
}
std::string SteelSeriesApex9Controller::GetVersion()
{
std::string return_string = "Unsupported protocol";
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE];
int result;
memset(obuf, 0x00, sizeof(obuf));
obuf[0x00] = 0;
obuf[0x01] = 0x90;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2);
if(result > 0)
{
std::string fwver(ibuf, ibuf+STEELSERIES_PACKET_IN_SIZE);
fwver = fwver.substr(2, fwver.size());
fwver = fwver.c_str();
/*---------------------------------------------*\
| Find 2 periods in string, if found we can |
| form a X.Y.Z revision. |
\*---------------------------------------------*/
std::size_t majorp = fwver.find('.');
if(majorp != std::string::npos)
{
std::size_t minorp = fwver.find('.', majorp+1);
if(minorp != std::string::npos)
{
std::string major = fwver.substr(0, majorp);
std::string minor = fwver.substr(majorp+1, (minorp-majorp-1));
std::string build = fwver.substr(minorp+1);
return_string = "KBD: " + major + "." + minor + "." + build;
}
}
}
return(return_string);
}
/*-------------------------------------------------------------------------------------------------*\
| Private packet sending functions. |
\*-------------------------------------------------------------------------------------------------*/
void SteelSeriesApex9Controller::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);
}

@ -0,0 +1,38 @@
/*---------------------------------------------------------*\
| SteelSeriesApex9Controller.h |
| |
| Driver for SteelSeries Apex 9 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "RGBController.h"
#include "SteelSeriesGeneric.h"
#include "SteelSeriesApexBaseController.h"
enum
{
APEX_9_PACKET_ID_DIRECT = 0x40, /* Direct mode */
APEX_9_PACKET_LENGTH = 513,
};
class SteelSeriesApex9Controller : public SteelSeriesApexBaseController
{
public:
SteelSeriesApex9Controller(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name);
~SteelSeriesApex9Controller();
void SetMode(unsigned char mode, std::vector<RGBColor> colors);
void SetLEDsDirect(std::vector<RGBColor> colors);
std::string GetSerial() override;
std::string GetVersion() override;
private:
void SelectProfile(unsigned char profile);
};

@ -27,8 +27,8 @@ public:
std::string GetLocation();
std::string GetName();
std::string GetSerial();
std::string GetVersion();
virtual std::string GetSerial();
virtual std::string GetVersion();
virtual void SetMode(unsigned char mode, std::vector<RGBColor> colors) = 0;

@ -106,7 +106,7 @@ void RGBController_SteelSeriesApex::SetupZones()
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))
if((proto_type == APEX) || (proto_type == APEX_M) || (proto_type == APEX_9_TKL) || (proto_type == APEX_9_MINI))
{
SetSkuRegion(*new_zone.matrix_map, sku);
}
@ -116,7 +116,7 @@ void RGBController_SteelSeriesApex::SetupZones()
new_zone.matrix_map = NULL;
}
if((proto_type == APEX) || (proto_type == APEX_M))
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];

@ -264,6 +264,58 @@ static const std::vector<matrix_region_patch> apex_tkl_us_region_patch =
{5, 20, NA},
};
/*-------------------------------------------------*\
| The Mini region patch is common for all Mini SKUs |
\*-------------------------------------------------*/
static const std::vector<matrix_region_patch> apex_mini_us_region_patch =
{
{0, 0, NA},
{0, 2, NA},
{0, 3, NA},
{0, 4, NA},
{0, 5, NA},
{0, 7, NA},
{0, 8, NA},
{0, 9, NA},
{0, 10, NA},
{0, 11, NA},
{0, 12, NA},
{0, 13, NA},
{0, 14, NA},
{0, 15, NA},
{0, 16, NA},
{0, 17, NA},
{1, 0, 37},
{1, 15, NA},
{1, 16, NA},
{1, 17, NA},
{1, 18, NA},
{1, 19, NA},
{1, 20, NA},
{1, 21, NA},
{2, 15, NA},
{2, 16, NA},
{2, 17, NA},
{2, 18, NA},
{2, 19, NA},
{2, 20, NA},
{2, 21, NA},
{3, 18, NA},
{3, 19, NA},
{3, 20, NA},
{4, 16, NA},
{4, 18, NA},
{4, 19, NA},
{4, 20, NA},
{4, 21, NA},
{5, 15, NA},
{5, 16, NA},
{5, 17, NA},
{5, 18, NA},
{5, 20, NA},
};
/*-----------------------------------------------*\
| Keyname lookups change the character displayed |
| on the LED view GUI by overriding the value |
@ -309,8 +361,27 @@ static const std::map<std::string, sku_patch> patch_lookup =
| base patch, then apply the regional patch on top (if any). |
\*----------------------------------------------------------*/
/*---------------------*\
| APEX Pro Gen 3 |
\*---------------------*/
{ "64660", { {}, {}, {} }},
{ "64740", { apex_tkl_us_region_patch, {}, {} }},
{ "64871", { apex_tkl_us_region_patch, {}, {} }},
{ "64913", { apex_mini_us_region_patch, {}, {} }},
/*---------------------*\
| APEX Pro Gen 2 / 2023 |
\*---------------------*/
{ "64856", { apex_tkl_us_region_patch, {}, {} }},
{ "64865", { apex_tkl_us_region_patch, {}, {} }},
{ "64820", { apex_mini_us_region_patch, {}, {} }},
{ "64842", { apex_mini_us_region_patch, {}, {} }},
/*--------*\
| APEX PRO |
| APEX Pro |
\*--------*/
{ "64739", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64738", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_nor_keyname_lookup }},
@ -323,6 +394,17 @@ static const std::map<std::string, sku_patch> patch_lookup =
{ "64629", { {}, apex_jp_region_patch, apex_jp_keyname_lookup }},
/*--------*\
| APEX 9 |
\*--------*/
{ "64847", { apex_tkl_us_region_patch, {}, {} }},
{ "64848", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64849", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_nor_keyname_lookup }},
// { "64850", { apex_tkl_us_region_patch, apex_iso_region_patch, {} }},
// { "64851", { apex_tkl_us_region_patch, apex_iso_region_patch, {} }},
{ "64837", { apex_mini_us_region_patch, {}, {} }},
/*--------*\
| APEX 7 |
\*--------*/
{ "64646", { apex_tkl_us_region_patch, {}, {} }},
@ -406,6 +488,42 @@ static void SetSkuLedNames (std::vector<led>& input, std::string& sku, unsigned
| SKU codes for all known Apex Pro / 7 / 5 & TKL variant |
| keyboards as at Janauary 2022. Generated by cross-checking |
| store listings aginst Steelseries website. |
| Updated 2025 for Apex 9 and Pro Gen 2 & Gen 3 |
| The product Pro Mini seem to belong to Gen 2 |
| |
| -- APEX PRO Gen 3 -- |
| |
| "64660", // US |
| |
| >> APEX PRO TKL Gen 3 |
| |
| "64740", // US TKL |
| |
| >> APEX PRO TKL Wireless Gen 3 |
| |
| "64871", // US TKL Wireless |
| |
| >> APEX PRO Mini Gen 3 |
| |
| "64913", // US Mini |
| |
| -- APEX PRO Gen2 / 2023 -- |
| |
| >> APEX PRO TKL Gen 2 / 2023 |
| |
| "64856", // US TKL |
| |
| >> APEX PRO TKL Wireless Gen 2 / 2023 |
| |
| "64865", // US TKL Wireless |
| |
| >> APEX PRO Mini Gen 2 / 2023 |
| |
| "64820", // US Mini |
| |
| >> APEX PRO Mini Wireless Gen 2 / 2023 |
| |
| "64842", // US Mini Wireless |
| |
| -- APEX PRO -- |
| |
@ -428,6 +546,24 @@ static void SetSkuLedNames (std::vector<led>& input, std::string& sku, unsigned
| "64738", // Nordic TKL |
| "64739", // UK TKL |
| |
| -- APEX 9 -- |
| |
| >> APEX 9 TKL |
| |
| "64847", // US TKL |
| "64848", // UK TKL |
| "64849", // Nordic TKL |
| "64850", // German TKL |
| "64851", // French TKL |
| |
| >> APEX 9 Mini |
| |
| "64837", // US Mini |
| "64838", // UK Mini |
| "64839", // German Mini |
| "64840", // French Mini |
| "64841", // Nordic Mini |
| |
| -- APEX 7 -- |
| |
| >> RED switches |

@ -14,6 +14,7 @@
#include "SteelSeriesAerox5Controller.h"
#include "SteelSeriesArctis5Controller.h"
#include "SteelSeriesApex8ZoneController.h"
#include "SteelSeriesApex9Controller.h"
#include "SteelSeriesApexController.h"
#include "SteelSeriesApexMController.h"
#include "SteelSeriesApexTZoneController.h"
@ -110,6 +111,8 @@
#define STEELSERIES_APEX_5_PID 0x161C
#define STEELSERIES_APEX_7_PID 0x1612
#define STEELSERIES_APEX_7_TKL_PID 0x1618
#define STEELSERIES_APEX_9_TKL_PID 0x1634
#define STEELSERIES_APEX_9_MINI_PID 0x1620
#define STEELSERIES_APEX_PRO_PID 0x1610
#define STEELSERIES_APEX_PRO_TKL_PID 0x1614
#define STEELSERIES_APEX_PRO_TKL_2023_PID 0x1628
@ -246,6 +249,28 @@ void DetectSteelSeriesApex(hid_device_info* info, const std::string& name)
}
}
void DetectSteelSeriesApex9(hid_device_info* info, const std::string& name, steelseries_type proto_type)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
SteelSeriesApex9Controller* controller = new SteelSeriesApex9Controller(dev, proto_type, info->path, name);
RGBController_SteelSeriesApex* rgb_controller = new RGBController_SteelSeriesApex(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
void DetectSteelSeriesApex9TKL(hid_device_info* info, const std::string& name)
{
DetectSteelSeriesApex9(info, name, APEX_9_TKL);
}
void DetectSteelSeriesApex9Mini(hid_device_info* info, const std::string& name)
{
DetectSteelSeriesApex9(info, name, APEX_9_MINI);
}
void DetectSteelSeriesApexM(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
@ -475,10 +500,12 @@ REGISTER_HID_DETECTOR_IPU("SteelSeries Apex 3 TKL", Dete
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 5", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_5_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 7", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_7_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 7 TKL", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_7_TKL_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 9 TKL", DetectSteelSeriesApex9TKL, STEELSERIES_VID, STEELSERIES_APEX_9_TKL_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 9 Mini", DetectSteelSeriesApex9Mini, STEELSERIES_VID, STEELSERIES_APEX_9_MINI_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL 2023", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex M750", DetectSteelSeriesApexM, STEELSERIES_VID, STEELSERIES_APEX_M750_PID, 2 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex (OG)/Apex Fnatic", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_OG_PID, 0 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 350", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_350_PID, 0 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro 3", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO3_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 350", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_350_PID, 0 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro 3", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO3_PID, 1 );

@ -43,4 +43,6 @@ typedef enum
AEROX_5_DIABLO_WIRELESS_WIRED = 0x15,
AEROX_9_WIRELESS = 0x16,
AEROX_9_WIRELESS_WIRED = 0x17,
APEX_9_TKL = 0x18,
APEX_9_MINI = 0x19,
} steelseries_type;

Loading…
Cancel
Save