diff --git a/Controllers/SteelSeriesController/RGBController_SteelSeriesApex.cpp b/Controllers/SteelSeriesController/RGBController_SteelSeriesApex.cpp index 77a21878..108d2b89 100644 --- a/Controllers/SteelSeriesController/RGBController_SteelSeriesApex.cpp +++ b/Controllers/SteelSeriesController/RGBController_SteelSeriesApex.cpp @@ -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(); } diff --git a/Controllers/SteelSeriesController/SteelSeriesApexBaseController.h b/Controllers/SteelSeriesController/SteelSeriesApexBaseController.h index 71a2d435..006b6e31 100644 --- a/Controllers/SteelSeriesController/SteelSeriesApexBaseController.h +++ b/Controllers/SteelSeriesController/SteelSeriesApexBaseController.h @@ -11,9 +11,13 @@ #include "SteelSeriesGeneric.h" #include +#include #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 ( diff --git a/Controllers/SteelSeriesController/SteelSeriesApexController.cpp b/Controllers/SteelSeriesController/SteelSeriesApexController.cpp index 02f4ffe1..463d4826 100644 --- a/Controllers/SteelSeriesController/SteelSeriesApexController.cpp +++ b/Controllers/SteelSeriesController/SteelSeriesApexController.cpp @@ -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) { diff --git a/Controllers/SteelSeriesController/SteelSeriesApexRegions.h b/Controllers/SteelSeriesController/SteelSeriesApexRegions.h new file mode 100644 index 00000000..336851cc --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexRegions.h @@ -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 +#include +#include +#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 base_patch; + std::vector region_patch; + std::map 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 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 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 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 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 apex_uk_keyname_lookup = +{ + {36, KEY_EN_ISO_ENTER}, +}; + +static const std::map 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 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::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& input, std::string& sku, unsigned int led_count) +{ + std::map::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::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 | +| | +\*-----------------------------------------------------------*/ diff --git a/Controllers/SteelSeriesController/SteelSeriesControllerDetect.cpp b/Controllers/SteelSeriesController/SteelSeriesControllerDetect.cpp index fde30fca..59ba990c 100644 --- a/Controllers/SteelSeriesController/SteelSeriesControllerDetect.cpp +++ b/Controllers/SteelSeriesController/SteelSeriesControllerDetect.cpp @@ -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 ); diff --git a/Controllers/SteelSeriesController/SteelSeriesGeneric.h b/Controllers/SteelSeriesController/SteelSeriesGeneric.h index 62f8a9e9..5b47a781 100644 --- a/Controllers/SteelSeriesController/SteelSeriesGeneric.h +++ b/Controllers/SteelSeriesController/SteelSeriesGeneric.h @@ -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; diff --git a/OpenRGB.pro b/OpenRGB.pro index 1dc088c4..903e6396 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -582,6 +582,7 @@ HEADERS += Controllers/SteelSeriesController/SteelSeriesAerox9Controller.h \ Controllers/SteelSeriesController/SteelSeriesApexBaseController.h \ Controllers/SteelSeriesController/SteelSeriesApexController.h \ + Controllers/SteelSeriesController/SteelSeriesApexRegions.h \ Controllers/SteelSeriesController/SteelSeriesApexMController.h \ Controllers/SteelSeriesController/SteelSeriesApexTZoneController.h \ Controllers/SteelSeriesController/SteelSeriesArctis5Controller.h \