Multi-region SKU support for Steelseries Apex Pro

This commit is contained in:
Joseph E 2022-11-06 03:07:59 +00:00 committed by Adam Honse
parent d5c1402c30
commit 2bb27223f4
7 changed files with 663 additions and 218 deletions

View file

@ -9,27 +9,7 @@
#include "RGBControllerKeyNames.h"
#include "RGBController_SteelSeriesApex.h"
using namespace std::chrono_literals;
//0xFFFFFFFF indicates an unused entry in matrix
#define NA 0xFFFFFFFF
static unsigned int matrix_map[6][23] =
{ { 37, NA, 53, 54, 55, 56, NA, 57, 58, 59, 60, NA, 61 , 62 , 63 , 64 , 65, 66, 67, NA, NA, NA, NA },
{ 48, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 41, 42 , NA , 38 , 88 , 68, 69, 70, 89, 90, 91, 92 },
{ 39, NA, 16, 22, 4 , 17, 19, 24, 20, 8 , 14, 15, 43 , 44 , NA , 36 , 71, 72, 73, 101, 102, 103, 93 },
{ 52, NA, 0 , 18, 3 , 5 , 6 , 7 , 9 , 10, 11, 46, 47 , 45 , NA , NA , NA, NA, NA, 98, 99, 100, NA },
{ 80, 78, 25, 23, 2 , 21, NA, 1 , 13, 12, 49, 50, 51 , NA , 84 , NA , NA, 77, NA, 95, 96, 97, 94 },
{ 79, 82, 81, NA, NA, NA, NA, 40, NA, NA, NA, NA, 85 , 86 , 87 , 83 , 75, 76, 74, 104, NA, 105, NA } };
static unsigned int matrix_map_tkl[6][19] =
{ { 37, NA, 53, 54, 55, 56, NA, 57, 58, 59, 60, NA, 61 , 62 , 63 , 64 , 65, 66, 67},
{ 48, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 41, 42 , NA , 38 , 88 , 68, 69, 70},
{ 39, NA, 16, 22, 4 , 17, 19, 24, 20, 8 , 14, 15, 43 , 44 , NA , 36 , 71, 72, 73},
{ 52, NA, 0 , 18, 3 , 5 , 6 , 7 , 9 , 10, 11, 46, 47 , 45 , NA , NA , NA, NA, NA},
{ 80, 78, 25, 23, 2 , 21, NA, 1 , 13, 12, 49, 50, 51 , NA , 84 , NA , NA, 77, NA},
{ 79, 82, 81, NA, NA, NA, NA, 40, NA, NA, NA, NA, 85 , 86, 87, 83 , 75, 76, 74} };
#include "SteelSeriesApexRegions.h"
static const char* zone_names[] =
{
@ -43,122 +23,7 @@ static zone_type zone_types[] =
static const unsigned int zone_sizes[] =
{
106,
};
static const unsigned int zone_sizes_tkl[] =
{
89,
};
static const char *led_names[] =
{
KEY_EN_A,
KEY_EN_B,
KEY_EN_C,
KEY_EN_D,
KEY_EN_E,
KEY_EN_F,
KEY_EN_G,
KEY_EN_H,
KEY_EN_I,
KEY_EN_J,
KEY_EN_K,
KEY_EN_L,
KEY_EN_M,
KEY_EN_N,
KEY_EN_O,
KEY_EN_P,
KEY_EN_Q,
KEY_EN_R,
KEY_EN_S,
KEY_EN_T,
KEY_EN_U,
KEY_EN_V,
KEY_EN_W,
KEY_EN_X,
KEY_EN_Y,
KEY_EN_Z,
KEY_EN_1,
KEY_EN_2,
KEY_EN_3,
KEY_EN_4,
KEY_EN_5,
KEY_EN_6,
KEY_EN_7,
KEY_EN_8,
KEY_EN_9,
KEY_EN_0,
KEY_EN_ANSI_ENTER,
KEY_EN_ESCAPE,
KEY_EN_BACKSPACE,
KEY_EN_TAB,
KEY_EN_SPACE,
KEY_EN_MINUS,
KEY_EN_EQUALS,
KEY_EN_LEFT_BRACKET,
KEY_EN_RIGHT_BRACKET,
KEY_EN_POUND,
KEY_EN_SEMICOLON,
KEY_EN_QUOTE,
KEY_EN_BACK_TICK,
KEY_EN_COMMA,
KEY_EN_PERIOD,
KEY_EN_FORWARD_SLASH,
KEY_EN_CAPS_LOCK,
KEY_EN_F1,
KEY_EN_F2,
KEY_EN_F3,
KEY_EN_F4,
KEY_EN_F5,
KEY_EN_F6,
KEY_EN_F7,
KEY_EN_F8,
KEY_EN_F9,
KEY_EN_F10,
KEY_EN_F11,
KEY_EN_F12,
KEY_EN_PRINT_SCREEN,
KEY_EN_SCROLL_LOCK,
KEY_EN_PAUSE_BREAK,
KEY_EN_INSERT,
KEY_EN_HOME,
KEY_EN_PAGE_UP,
KEY_EN_DELETE,
KEY_EN_END,
KEY_EN_PAGE_DOWN,
KEY_EN_RIGHT_ARROW,
KEY_EN_LEFT_ARROW,
KEY_EN_DOWN_ARROW,
KEY_EN_UP_ARROW,
KEY_EN_ISO_BACK_SLASH,
KEY_EN_LEFT_CONTROL,
KEY_EN_LEFT_SHIFT,
KEY_EN_LEFT_ALT,
KEY_EN_LEFT_WINDOWS,
KEY_EN_RIGHT_CONTROL,
KEY_EN_RIGHT_SHIFT,
KEY_EN_RIGHT_ALT,
KEY_EN_RIGHT_WINDOWS,
KEY_EN_RIGHT_FUNCTION,
KEY_EN_ANSI_BACK_SLASH,
KEY_EN_NUMPAD_LOCK,
KEY_EN_NUMPAD_DIVIDE,
KEY_EN_NUMPAD_TIMES,
KEY_EN_NUMPAD_MINUS,
KEY_EN_NUMPAD_PLUS,
KEY_EN_NUMPAD_ENTER,
KEY_EN_NUMPAD_1,
KEY_EN_NUMPAD_2,
KEY_EN_NUMPAD_3,
KEY_EN_NUMPAD_4,
KEY_EN_NUMPAD_5,
KEY_EN_NUMPAD_6,
KEY_EN_NUMPAD_7,
KEY_EN_NUMPAD_8,
KEY_EN_NUMPAD_9,
KEY_EN_NUMPAD_0,
KEY_EN_NUMPAD_PERIOD,
sizeof(led_names)/sizeof(char*),
};
/**------------------------------------------------------------------*\
@ -168,7 +33,7 @@ static const char *led_names[] =
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectSteelSeriesApex,DetectSteelSeriesApexTKL,DetectSteelSeriesApexM
@detectors DetectSteelSeriesApex,DetectSteelSeriesApexM
@comment
\*-------------------------------------------------------------------*/
@ -181,7 +46,8 @@ RGBController_SteelSeriesApex::RGBController_SteelSeriesApex(SteelSeriesApexBase
type = DEVICE_TYPE_KEYBOARD;
description = "SteelSeries Apex RGB Device";
location = controller->GetDeviceLocation();
serial = "";
serial = controller->GetSerialString();
version = controller->GetVersionString();
proto_type = controller->proto_type;
@ -204,6 +70,7 @@ RGBController_SteelSeriesApex::~RGBController_SteelSeriesApex()
{
if(zones[zone_index].matrix_map != NULL)
{
free(zones[zone_index].matrix_map->map);
delete zones[zone_index].matrix_map;
}
}
@ -216,39 +83,30 @@ 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((proto_type == APEX) || (proto_type == APEX_M))
{
new_zone.leds_min = zone_sizes[zone_idx];
new_zone.leds_max = zone_sizes[zone_idx];
new_zone.leds_count = zone_sizes[zone_idx];
}
else
{
new_zone.leds_min = zone_sizes_tkl[zone_idx];
new_zone.leds_max = zone_sizes_tkl[zone_idx];
new_zone.leds_count = zone_sizes_tkl[zone_idx];
}
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->height = 6;
new_zone.matrix_map->map = (unsigned int *) malloc(matrix_mapsize*sizeof(unsigned int));
if((proto_type == APEX) || (proto_type == APEX_M))
{
new_zone.matrix_map->width = 23;
new_zone.matrix_map->map = (unsigned int *)&matrix_map;
}
else
{
new_zone.matrix_map->width = 19;
new_zone.matrix_map->map = (unsigned int *)&matrix_map_tkl;
SetSkuRegion(*new_zone.matrix_map, sku);
}
}
else
@ -256,26 +114,17 @@ void RGBController_SteelSeriesApex::SetupZones()
new_zone.matrix_map = NULL;
}
zones.push_back(new_zone);
if((proto_type == APEX) || (proto_type == APEX_M))
{
total_led_count += zone_sizes[zone_idx];
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];
}
else
{
total_led_count += zone_sizes_tkl[zone_idx];
}
}
for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++)
{
led new_led;
new_led.name = led_names[led_idx];
leds.push_back(new_led);
}
zones.push_back(new_zone);
};
SetSkuLedNames(leds, sku, total_led_count);
SetupColors();
}

View file

@ -11,9 +11,13 @@
#include "SteelSeriesGeneric.h"
#include <string>
#include <cstring>
#pragma once
#define STEELSERIES_PACKET_IN_SIZE 64
#define STEELSERIES_PACKET_OUT_SIZE STEELSERIES_PACKET_IN_SIZE + 1
class SteelSeriesApexBaseController
{
public:
@ -28,18 +32,124 @@ public:
return("HID: " + location);
};
steelseries_type proto_type;
/*----------------------------------------*\
| The serial number of the keyboard is |
| acquired by sending an output report |
| to address 0xFF and reading the result. |
| The HID capability table is not used. |
| The serial number also contains the model|
| number which can be used to determine |
| the physical layout of different region |
| keyboards throughout the product stack. |
\*----------------------------------------*/
std::string GetSerialString()
{
wchar_t serial_string[128];
hid_get_serial_number_string(dev, serial_string, 128);
std::string return_string = "";
if(proto_type == APEX)
{
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE];
int result;
std::wstring return_wstring = serial_string;
std::string return_string(return_wstring.begin(), return_wstring.end());
memset(obuf, 0x00, sizeof(obuf));
obuf[0x00] = 0;
obuf[0x01] = 0xFF;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
return(return_string);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2);
/*---------------------------------------*\
| Only the first 19 bytes are of value |
\*---------------------------------------*/
if( result > 0)
{
std::string serialnum(ibuf, ibuf+19);
return_string = serialnum;
}
}
return(return_string);
}
steelseries_type proto_type;
std::string GetVersionString()
{
std::string return_string = "Unsupported protocol";
if(proto_type == APEX)
{
/*--------------------------------------------*\
| For the Apex Pro there are two firmware |
| versions which can be acquired, KBD and LED. |
| We know where both are located, we do not |
| know which is what. For now we'll make an |
| assumption and fix if proven wrong. |
\*--------------------------------------------*/
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);
/*---------------------------------------*\
| 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;
}
}
}
/*---------------------------------------*\
| Clear and reuse buffer |
\*---------------------------------------*/
memset(ibuf, 0x00, sizeof(ibuf));
obuf[0x02] = 0x01;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 10);
if(result > 0)
{
std::string fwver(ibuf, ibuf+STEELSERIES_PACKET_IN_SIZE);
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 = return_string + " / LED: " + major + "." + minor + "." + build;
}
}
}
}
return(return_string);
}
virtual void SetMode
(

View file

@ -13,17 +13,18 @@
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, 0x53,
0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, //100
0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63 };
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 };
SteelSeriesApexController::SteelSeriesApexController(hid_device* dev_handle, steelseries_type type, const char* path)
{

View file

@ -0,0 +1,497 @@
/*-----------------------------------------*\
| SteelSeriesApexRegions.h |
| |
| Region specific SKU settings for the |
| Apex 5/7/Pro and TKL keyboards |
| |
| Joseph East (dripsnek) 2021 |
\*-----------------------------------------*/
#pragma once
#define NA 0xFFFFFFFF
#include <string>
#include <map>
#include <vector>
#include "RGBController_SteelSeriesApex.h"
#include "RGBControllerKeyNames.h"
/*----------------------------------------------------------------------*\
| As of firmware 4.1.0 there are in total 111 possible standard keys |
| which are shared across the Apex Pro / 7 / TKL / 5 and their regional |
| SKUs in addition to the 6 media keys. No SKU has all 111, however |
| regardless of the physial layout, the one configuration can be used |
| across all SKUs with missing keys simply having no effect. |
| The complication comes in the visualisation as different key layouts |
| change the LED positions, additionally some labels / scancodes are |
| overloaded based on the language which clashes with the OpenRGB |
| defaults. In order to account for this a base SKU (ANSI) is assumed |
| which is transformed into a regional SKU when device detection returns |
| a known SKU number from the first 5 characters of the serial number. |
\*----------------------------------------------------------------------*/
#define MATRIX_HEIGHT 6
#define MATRIX_WIDTH 22
/*-------------------------------------------------------*\
| Default keymap where values are indicies into led_names |
| or NA for no key. |
\*-------------------------------------------------------*/
#define MATRIX_MAP_ANSI\
{ { 37, NA, 53, 54, 55, 56, NA, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, NA, NA, NA, NA },\
{ 48, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 41, 42, 38, NA, 68, 69, 70, 94, 95, 96, 97 },\
{ 39, NA, 16, 22, 4 , 17, 19, 24, 20, 8 , 14, 15, 43, 44, 88, 71, 72, 73, 106, 107, 108, 98 },\
{ 52, NA, 0 , 18, 3 , 5 , 6 , 7 , 9 , 10, 11, 46, 47, 36, NA, NA, NA, NA, 103, 104, 105, NA },\
{ 80, NA, 25, 23, 2 , 21, 1 , 13, 12, 49, 50, 51, 84, NA, NA, NA, 77, NA, 100, 101, 102, 99 },\
{ 79, 82, 81, NA, NA, NA, NA, 40, NA, NA, NA, 85, 86, 87, 83, 75, 76, 74, 109, NA, 110, NA } };
static const int matrix_mapsize = MATRIX_HEIGHT * MATRIX_WIDTH;
static const char* led_names[] =
{
KEY_EN_A,
KEY_EN_B,
KEY_EN_C,
KEY_EN_D,
KEY_EN_E,
KEY_EN_F,
KEY_EN_G,
KEY_EN_H,
KEY_EN_I,
KEY_EN_J,
KEY_EN_K,
KEY_EN_L,
KEY_EN_M,
KEY_EN_N,
KEY_EN_O,
KEY_EN_P,
KEY_EN_Q,
KEY_EN_R,
KEY_EN_S,
KEY_EN_T,
KEY_EN_U,
KEY_EN_V,
KEY_EN_W,
KEY_EN_X,
KEY_EN_Y,
KEY_EN_Z,
KEY_EN_1,
KEY_EN_2,
KEY_EN_3,
KEY_EN_4,
KEY_EN_5,
KEY_EN_6,
KEY_EN_7,
KEY_EN_8,
KEY_EN_9,
KEY_EN_0,
KEY_EN_ANSI_ENTER,
KEY_EN_ESCAPE,
KEY_EN_BACKSPACE,
KEY_EN_TAB,
KEY_EN_SPACE,
KEY_EN_MINUS,
KEY_EN_EQUALS,
KEY_EN_LEFT_BRACKET,
KEY_EN_RIGHT_BRACKET,
KEY_EN_POUND,
KEY_EN_SEMICOLON,
KEY_EN_QUOTE,
KEY_EN_BACK_TICK,
KEY_EN_COMMA,
KEY_EN_PERIOD,
KEY_EN_FORWARD_SLASH,
KEY_EN_CAPS_LOCK,
KEY_EN_F1,
KEY_EN_F2,
KEY_EN_F3,
KEY_EN_F4,
KEY_EN_F5,
KEY_EN_F6,
KEY_EN_F7,
KEY_EN_F8,
KEY_EN_F9,
KEY_EN_F10,
KEY_EN_F11,
KEY_EN_F12,
KEY_EN_PRINT_SCREEN,
KEY_EN_SCROLL_LOCK,
KEY_EN_PAUSE_BREAK,
KEY_EN_INSERT,
KEY_EN_HOME,
KEY_EN_PAGE_UP,
KEY_EN_DELETE,
KEY_EN_END,
KEY_EN_PAGE_DOWN,
KEY_EN_RIGHT_ARROW,
KEY_EN_LEFT_ARROW,
KEY_EN_DOWN_ARROW,
KEY_EN_UP_ARROW,
KEY_EN_ISO_BACK_SLASH,
KEY_EN_LEFT_CONTROL,
KEY_EN_LEFT_SHIFT,
KEY_EN_LEFT_ALT,
KEY_EN_LEFT_WINDOWS,
KEY_EN_RIGHT_CONTROL,
KEY_EN_RIGHT_SHIFT,
KEY_EN_RIGHT_ALT,
KEY_EN_RIGHT_WINDOWS,
KEY_EN_RIGHT_FUNCTION,
KEY_EN_ANSI_BACK_SLASH,
KEY_JP_RO,
KEY_JP_KANA,
KEY_JP_YEN,
KEY_JP_HENKAN,
KEY_JP_MUHENKAN,
KEY_EN_NUMPAD_LOCK,
KEY_EN_NUMPAD_DIVIDE,
KEY_EN_NUMPAD_TIMES,
KEY_EN_NUMPAD_MINUS,
KEY_EN_NUMPAD_PLUS,
KEY_EN_NUMPAD_ENTER,
KEY_EN_NUMPAD_1,
KEY_EN_NUMPAD_2,
KEY_EN_NUMPAD_3,
KEY_EN_NUMPAD_4,
KEY_EN_NUMPAD_5,
KEY_EN_NUMPAD_6,
KEY_EN_NUMPAD_7,
KEY_EN_NUMPAD_8,
KEY_EN_NUMPAD_9,
KEY_EN_NUMPAD_0,
KEY_EN_NUMPAD_PERIOD,
};
struct matrix_region_patch
{
int row;
int column;
unsigned int value;
};
struct sku_patch
{
std::vector<matrix_region_patch> base_patch;
std::vector<matrix_region_patch> region_patch;
std::map<int, std::string> key_patch;
};
/*-----------------------------------------------*\
| There are two types of patches, a SKU may |
| use one, both or none. The first type are |
| region patches which modify the location of |
| a logical key (in terms of RGB) based on the |
| scancode emitted by the physical key on the |
| keyboard. This is the most important as it |
| impacts what keys are lit in terms of RGB. |
| |
| The second type are keyname lookups |
| where the face of the key may be different |
| between SKUs but the scancode is the same, this |
| is for convenience of the LED view in the GUI. |
| |
| Each SKU has up to 3 operations performed on |
| it. A generic TKL region patch (where req'd) |
| the SKU region patch then a keyname lookup in |
| that order. |
\*-----------------------------------------------*/
/*-----------------------------------------------*\
| Region patches are structs consisting of |
| {row, column, value} where row and column are |
| 0 indexed into the logical key layout defined |
| by MATRIX_MAP_ANSI. This array closely matches |
| the physical layout of the keyboard. Value is |
| an index into the led_names array and refers |
| to the replacement character which should exist |
| at that location, or NA for no character. When |
| the SetSkuRegion function is called, the |
| contents of MATRIX_MAP_ANSI populate a local |
| array, then based on the SKU argument the |
| specific patches are applied to the local copy |
| before being passed to the controller. |
\*-----------------------------------------------*/
static const std::vector<matrix_region_patch> apex_jp_region_patch =
{
{1, 13, 91},
{1, 14, 38},
{2, 14, 36},
{3, 13, 45},
{4, 12, 89},
{4, 13, 84},
{5, 3, 93},
{5, 10, 92},
{5, 11, 90},
{5, 12, 85},
};
static const std::vector<matrix_region_patch> apex_iso_region_patch =
{
{2, 14, 36},
{3, 13, 45},
{4, 1, 78},
};
/*-----------------------------------------------*\
| The TKL region patch is common for all TKL SKUs |
\*-----------------------------------------------*/
static const std::vector<matrix_region_patch> apex_tkl_us_region_patch =
{
{0, 15, NA},
{0, 16, NA},
{0, 17, NA},
{1, 18, NA},
{1, 19, NA},
{1, 20, NA},
{1, 21, NA},
{2, 18, NA},
{2, 19, NA},
{2, 20, NA},
{2, 21, NA},
{3, 18, NA},
{3, 19, NA},
{3, 20, NA},
{4, 18, NA},
{4, 19, NA},
{4, 20, NA},
{4, 21, NA},
{5, 18, NA},
{5, 20, NA},
};
/*-----------------------------------------------*\
| Keyname lookups change the character displayed |
| on the LED view GUI by overriding the value |
| associated with the index in led_names. |
\*-----------------------------------------------*/
static const std::map<int, std::string> apex_jp_keyname_lookup =
{
{42, KEY_JP_CHEVRON},
{43, KEY_JP_AT},
{44, KEY_EN_LEFT_BRACKET},
{45, KEY_EN_RIGHT_BRACKET},
{47, KEY_JP_COLON},
{48, KEY_JP_EJ},
{84, KEY_EN_RIGHT_SHIFT},
{36, KEY_EN_ISO_ENTER},
};
static const std::map<int, std::string> apex_uk_keyname_lookup =
{
{36, KEY_EN_ISO_ENTER},
};
static const std::map<int, std::string> apex_nor_keyname_lookup =
{
{36, KEY_EN_ISO_ENTER},
{41, KEY_NORD_PLUS},
{42, KEY_NORD_BACKSLASH},
{43, KEY_NORD_AAL},
{44, KEY_NORD_CHEVRON},
{45, KEY_NORD_QUOTE},
{46, KEY_NORD_OE},
{47, KEY_NORD_AE},
{48, KEY_NORD_HALF},
{51, KEY_NORD_HYPHEN},
{78, KEY_NORD_ANGLE_BRACKET},
};
static const std::map<std::string, sku_patch> patch_lookup =
{
/*----------------------------------------------------------*\
| All TKL keyboards must use apex_tkl_us_region_patch as the |
| base patch, then apply the regional patch on top (if any). |
\*----------------------------------------------------------*/
/*--------*\
| 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 }},
{ "64737", { apex_tkl_us_region_patch, apex_jp_region_patch, apex_jp_keyname_lookup }},
{ "64734", { apex_tkl_us_region_patch, {}, {} }},
{ "64631", { {}, apex_iso_region_patch, apex_nor_keyname_lookup }},
{ "64634", { {}, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64629", { {}, apex_jp_region_patch, apex_jp_keyname_lookup }},
/*--------*\
| APEX 7 |
\*--------*/
{ "64646", { apex_tkl_us_region_patch, {}, {} }},
{ "64758", { apex_tkl_us_region_patch, {}, {} }},
{ "64747", { apex_tkl_us_region_patch, {}, {} }},
{ "64652", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64760", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64749", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64651", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_nor_keyname_lookup }},
{ "64649", { apex_tkl_us_region_patch, apex_jp_region_patch, apex_jp_keyname_lookup }},
{ "64756", { apex_tkl_us_region_patch, apex_jp_region_patch, apex_jp_keyname_lookup }},
{ "64635", { {}, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64778", { {}, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64788", { {}, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64641", { {}, apex_iso_region_patch, apex_nor_keyname_lookup }},
{ "64775", { {}, apex_iso_region_patch, apex_nor_keyname_lookup }},
{ "64787", { {}, apex_iso_region_patch, apex_nor_keyname_lookup }},
{ "64639", { {}, apex_jp_region_patch, apex_jp_keyname_lookup }},
{ "64772", { {}, apex_jp_region_patch, apex_jp_keyname_lookup }},
/*--------*\
| APEX 5 |
\*--------*/
{ "64534", { {}, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64537", { {}, apex_jp_region_patch, apex_jp_keyname_lookup }},
{ "64533", { {}, apex_iso_region_patch, apex_nor_keyname_lookup }},
};
static void SetSkuRegion (matrix_map_type& input, std::string& sku)
{
std::map<std::string, sku_patch>::const_iterator it = patch_lookup.find(sku);
unsigned int local_matrix [MATRIX_HEIGHT][MATRIX_WIDTH] = MATRIX_MAP_ANSI;
input.height = MATRIX_HEIGHT;
input.width = MATRIX_WIDTH;
if(it != patch_lookup.end())
{
for(std::size_t i = 0; i < it->second.base_patch.size(); i++)
{
local_matrix[it->second.base_patch[i].row][it->second.base_patch[i].column] = it->second.base_patch[i].value;
}
for(std::size_t i = 0; i < it->second.region_patch.size(); i++)
{
local_matrix[it->second.region_patch[i].row][it->second.region_patch[i].column] = it->second.region_patch[i].value;
}
}
memcpy(input.map, (unsigned int *)local_matrix, sizeof(unsigned int)*MATRIX_HEIGHT*MATRIX_WIDTH);
}
static void SetSkuLedNames (std::vector<led>& input, std::string& sku, unsigned int led_count)
{
std::map<std::string, sku_patch>::const_iterator it = patch_lookup.find(sku);
for(unsigned int led_idx = 0; led_idx < led_count; led_idx++)
{
led new_led;
if(it != patch_lookup.end())
{
std::map<int, std::string>::const_iterator jt = it->second.key_patch.find(led_idx);
if(jt == it->second.key_patch.end())
{
new_led.name = led_names[led_idx];
}
else if(jt != it->second.key_patch.end())
{
new_led.name = jt->second;
}
}
else
{
new_led.name = led_names[led_idx];
}
input.push_back(new_led);
}
}
/*-----------------------------------------------------------*\
| 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. |
| |
| -- APEX PRO -- |
| |
| "64626", // US |
| "64627", // German |
| "64628", // French |
| "64629", // Japanese |
| "64630", // Korean |
| "64631", // Nordic |
| "64632", // Taiwanese |
| "64633", // Thai |
| "64634" // UK |
| |
| >> APEX PRO TKL |
| |
| "64734", // US TKL |
| "64735", // German TKL |
| "64736", // French TKL |
| "64737", // Japanese TKL |
| "64738", // Nordic TKL |
| "64739", // UK TKL |
| |
| -- APEX 7 -- |
| |
| >> RED switches |
| |
| "64635", // UK Red |
| "64636", // US Red |
| "64637", // German Red |
| "64638", // French Red |
| "64639", // Japanese Red |
| "64640", // Korean Red |
| "64641", // Nordic Red |
| "64642", // Russian Red |
| "64643", // Thai Red |
| "64644", // Turkish Red |
| "64645", // Taiwanese Red |
| |
| >> RED TKL |
| |
| "64646" // US Red TKL |
| "64647", // German Red TKL |
| "64648", // French Red TKL |
| "64649", // Japanese Red TKL |
| "64650", // Korean Red TKL |
| "64651", // Nordic Red TKL |
| "64652", // UK Red TKL |
| |
| >> BLUE switches |
| |
| "64770" // German Blue |
| "64771" // French Blue |
| "64772" // Japanese Blue |
| "64773" // Korean Blue |
| "64774" // US Blue |
| "64775" // Nordic Blue |
| "64776" // Thai Blue |
| "64777" // Taiwanese Blue |
| "64778" // UK Blue |
| |
| >> BLUE TKL |
| |
| "64756" // Japanese Blue TKL |
| "64757" // Korean Blue TKL |
| "64758" // US Blue TKL |
| "64760" // UK Blue TKL |
| |
| >> BROWN switches |
| |
| "64784" // German Brown |
| "64785" // French Brown |
| "64786" // US Brown |
| "64787" // Nordic Brown |
| "64788" // UK Brown |
| |
| >> BROWN TKL |
| |
| "64746" // French Brown TKL |
| "64747" // US Brown TKL |
| "64749" // UK Brown TKL |
| |
| -- APEX 5 -- |
| |
| "64533", // Nordic |
| "64534", // UK |
| "64535", // German |
| "64536", // French |
| "64537", // Japanese |
| "64538", // Turkish |
| "64539", // US |
| |
\*-----------------------------------------------------------*/

View file

@ -134,18 +134,6 @@ void DetectSteelSeriesApex(hid_device_info* info, const std::string& name)
}
}
void DetectSteelSeriesApexTKL(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
SteelSeriesApexController* controller = new SteelSeriesApexController(dev, APEX_TKL, info->path);
RGBController_SteelSeriesApex* rgb_controller = new RGBController_SteelSeriesApex(controller);
rgb_controller->name = name;
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
void DetectSteelSeriesApexM(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
@ -345,9 +333,9 @@ REGISTER_HID_DETECTOR_I("SteelSeries QCK Prism Cloth 4XL",
REGISTER_HID_DETECTOR_I("SteelSeries Apex 3", DetectSteelSeriesApexTZone, STEELSERIES_VID, STEELSERIES_APEX_3_PID, 3 );
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", DetectSteelSeriesApexTKL, STEELSERIES_VID, STEELSERIES_APEX_7_TKL_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 Pro", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_PID, 1 );
REGISTER_HID_DETECTOR_I("SteelSeries Apex Pro TKL", DetectSteelSeriesApexTKL, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_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 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 );

View file

@ -22,14 +22,13 @@ typedef enum
RIVAL_650 = 0x02,
SIBERIA_350 = 0x03,
APEX = 0x04,
APEX_TKL = 0x05,
APEX_M = 0x06,
APEX_OLD = 0x07,
SENSEI = 0x08,
RIVAL_600 = 0x09,
RIVAL_3 = 0x0a,
APEX_TZONE = 0x0b,
RIVAL_700 = 0x0c,
AEROX_3 = 0x0d,
APEX_M = 0x05,
APEX_OLD = 0x06,
SENSEI = 0x07,
RIVAL_600 = 0x08,
RIVAL_3 = 0x09,
APEX_TZONE = 0x0A,
RIVAL_700 = 0x0B,
AEROX_3 = 0x0C,
} steelseries_type;