Add HyperX Alloy Elite 2 support

Squash commits and code style changes, minor reworks to match Alloy Elite 1 code by Adam Honse <calcprogrammer1@gmail.com>
This commit is contained in:
KundaPanda 2021-04-02 01:43:42 +02:00 committed by Adam Honse
parent 2ae12c52ec
commit b572c15cce
6 changed files with 617 additions and 7 deletions

View file

@ -0,0 +1,173 @@
/*-----------------------------------------*\
| HyperXAlloyElite2Controller.cpp |
| |
| Driver for HyperX Alloy Elite2 RGB |
| Keyboard lighting controller |
| |
| KundaPanda (vojdo) 02/04/2021 |
\*-----------------------------------------*/
#include "HyperXAlloyElite2Controller.h"
#include <cstring>
/*-----------------------------------------*\
| Skip these indices in the color output |
\*-----------------------------------------*/
static const unsigned int SKIP_INDICES[] = { 6, 23, 29, 41, 47, 70, 71, 75, 76, 77, 87, 88, 93, 99, 100, 102, 108, 113 };
HyperXAlloyElite2Controller::HyperXAlloyElite2Controller(hid_device* dev_handle, const char* path)
{
dev = dev_handle;
location = path;
}
HyperXAlloyElite2Controller::~HyperXAlloyElite2Controller()
{
hid_close(dev);
}
std::string HyperXAlloyElite2Controller::GetDeviceLocation()
{
return("HID " + location);
}
std::string HyperXAlloyElite2Controller::GetSerialString()
{
wchar_t serial_string[128];
hid_get_serial_number_string(dev, serial_string, 128);
std::wstring return_wstring = serial_string;
std::string return_string(return_wstring.begin(), return_wstring.end());
return(return_string);
}
void HyperXAlloyElite2Controller::SetLEDsDirect(const std::vector<RGBColor>& colors)
{
unsigned char buf[65];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
/*-----------------------------------------------------*\
| Variables to keep track of color sending and skipping |
\*-----------------------------------------------------*/
size_t buf_idx = 1;
size_t color_idx = 0;
size_t packets_sent = 0;
size_t skipped = 0;
const unsigned int* skip_idx = &SKIP_INDICES[0];
/*-----------------------------------------------------*\
| Initialize direct control |
\*-----------------------------------------------------*/
SendDirectInitialization();
/*-----------------------------------------------------*\
| Continue filling and sending packets while color data |
| remains |
\*-----------------------------------------------------*/
while(color_idx < colors.size())
{
/*-------------------------------------------------*\
| Packets have colors in groups of 4 bytes, with |
| the first byte being 0x81 and then R, G, B. |
\*-------------------------------------------------*/
buf[buf_idx] = 0x81;
/*-------------------------------------------------*\
| If at a skipped index, add null data to packet |
| and increment skipped count and index |
| Otherwise, copy color data to buffer and increment|
| color index |
\*-------------------------------------------------*/
if(*skip_idx == color_idx + skipped)
{
buf[buf_idx + 1] = 0;
buf[buf_idx + 2] = 0;
buf[buf_idx + 3] = 0;
skip_idx++;
if(skip_idx >= SKIP_INDICES + sizeof(SKIP_INDICES) / sizeof(unsigned int))
{
skip_idx = SKIP_INDICES;
}
skipped++;
}
else
{
buf[buf_idx + 1] = RGBGetRValue(colors[color_idx]);
buf[buf_idx + 2] = RGBGetGValue(colors[color_idx]);
buf[buf_idx + 3] = RGBGetBValue(colors[color_idx]);
color_idx++;
}
/*-------------------------------------------------*\
| Increment packet buffer index by 4 bytes |
\*-------------------------------------------------*/
buf_idx += 4;
/*-------------------------------------------------*\
| If the packet buffer is full, send it and reset |
| buffer indexing |
| OR |
| If all colors have been filled into the buffer, |
| send the packet |
\*-------------------------------------------------*/
if((buf_idx >= sizeof(buf)) || (color_idx == colors.size()))
{
/*---------------------------------------------*\
| Send packet |
\*---------------------------------------------*/
hid_send_feature_report(dev, buf, sizeof(buf));
/*---------------------------------------------*\
| Zero out buffer and reset index |
\*---------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
buf_idx = 1;
/*---------------------------------------------*\
| Increment packet counter |
\*---------------------------------------------*/
packets_sent++;
}
}
/*-----------------------------------------------------*\
| Send empty packets until 9 total packets have been |
| sent |
\*-----------------------------------------------------*/
for(size_t remaining_packets = 0; packets_sent + remaining_packets < 9; remaining_packets++)
{
hid_send_feature_report(dev, buf, sizeof(buf));
}
}
void HyperXAlloyElite2Controller::SendDirectInitialization()
{
unsigned char buf[65];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
/*-----------------------------------------------------*\
| Set up Select Profile packet |
\*-----------------------------------------------------*/
buf[0x00] = 0x00;
buf[0x01] = 0x04;
buf[0x02] = 0xF2;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_send_feature_report(dev, buf, 65);
}

View file

@ -0,0 +1,33 @@
/*-----------------------------------------*\
| HyperXAlloyElite2Controller.h |
| |
| Definitions and types for HyperX Alloy |
| Elite2 RGB Keyboard lighting controller |
| |
| KundaPanda (vojdo) 02/04/2021 |
\*-----------------------------------------*/
#include "RGBController.h"
#include <string>
#include <hidapi/hidapi.h>
#pragma once
class HyperXAlloyElite2Controller
{
public:
HyperXAlloyElite2Controller(hid_device* dev_handle, const char* path);
~HyperXAlloyElite2Controller();
std::string GetDeviceLocation();
std::string GetSerialString();
void SetLEDsDirect(const std::vector<RGBColor>& colors);
private:
hid_device* dev;
std::string location;
void SendDirectInitialization();
};

View file

@ -1,7 +1,9 @@
#include "Detector.h"
#include "HyperXAlloyElite2Controller.h"
#include "HyperXAlloyOriginsController.h"
#include "HyperXKeyboardController.h"
#include "RGBController.h"
#include "RGBController_HyperXAlloyElite2.h"
#include "RGBController_HyperXAlloyOrigins.h"
#include "RGBController_HyperXKeyboard.h"
#include <hidapi/hidapi.h>
@ -11,6 +13,7 @@
\*-----------------------------------------------------*/
#define HYPERX_KEYBOARD_VID 0x0951
#define HYPERX_ALLOY_ELITE_PID 0x16BE
#define HYPERX_ALLOY_ELITE_2_PID 0x1711
#define HYPERX_ALLOY_FPS_RGB_PID 0x16DC
#define HYPERX_ALLOY_ORIGINS_PID 0x16E5
#define HYPERX_ALLOY_ORIGINS_CORE_PID 0x16E6
@ -38,14 +41,27 @@ void DetectHyperXAlloyOrigins(hid_device_info* info, const std::string& name)
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
void DetectHyperXAlloyElite2(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if( dev )
{
HyperXAlloyElite2Controller* controller = new HyperXAlloyElite2Controller(dev, info->path);
RGBController_HyperXAlloyElite2* rgb_controller = new RGBController_HyperXAlloyElite2(controller);
rgb_controller->name = name;
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite RGB", DetectHyperXKeyboards, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_PID, 2, 0xFF01);
REGISTER_HID_DETECTOR_IP("HyperX Alloy FPS RGB", DetectHyperXKeyboards, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_FPS_RGB_PID, 2, 0xFF01);
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite RGB", DetectHyperXKeyboards, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_PID, 2, 0xFF01);
REGISTER_HID_DETECTOR_IP("HyperX Alloy FPS RGB", DetectHyperXKeyboards, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_FPS_RGB_PID, 2, 0xFF01);
#ifdef _WIN32
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_PID, 3);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 3);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_PID, 3);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 3);
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite 2", DetectHyperXAlloyElite2, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_PID, 3, 0xFF90);
#else
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 0);
#endif
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 0);
REGISTER_HID_DETECTOR_I("HyperX Alloy Elite 2", DetectHyperXAlloyElite2, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_PID, 0);
#endif

View file

@ -0,0 +1,342 @@
/*-----------------------------------------*\
| RGBController_HyperXAlloyElite2.cpp |
| |
| Generic RGB Interface for HyperX Alloy |
| Elite2 RGB Keyboard |
| |
| KundaPanda (vojdo) 02/04/2021 |
\*-----------------------------------------*/
#include "RGBController_HyperXAlloyElite2.h"
using namespace std::chrono_literals;
//0xFFFFFFFF indicates an unused entry in matrix
#define NA 0xFFFFFFFF
static unsigned int matrix_map[8][22] =
{
{ NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 102, 105, 106, 96, NA, NA },
{ 108, 109, 110, 111, 112, NA, 113, 114, 115, NA, 116, 117, 118, NA, 119, 120, 121, NA, 122, 123, 124, 125 },
{ 0, 11, 17, 22, 27, NA, 33, 38, 43, 49, 55, 61, 65, 68, NA, 74, 78, 83, NA, NA, NA, NA },
{ 1, 6, 12, 18, 23, 28, 34, 39, 44, 50, 56, 62, 66, 69, NA, 75, 79, 84, 87, 92, 97, 103 },
{ 2, NA, 7, 13, 19, 24, 29, 35, 40, 45, 51, 57, 63, 67, 70, 76, 80, 85, 88, 93, 98, 104 },
{ 3, NA, 8, 14, 20, 25, 30, 36, 41, 46, 52, 58, 64, NA, 71, NA, NA, NA, 89, 94, 99, NA },
{ 4, NA, NA, 9, 15, 21, 26, 31, 37, 42, 47, 53, 59, 72, NA, NA, 81, NA, 90, 95, 100, 107 },
{ 5, 10, 16, NA, NA, NA, NA, 32, NA, NA, NA, 48, 60, 54, 73, 77, 82, 86, 91, NA, 101, NA }
};
static const char* zone_names[] =
{
"Keyboard",
};
static zone_type zone_types[] =
{
ZONE_TYPE_MATRIX,
};
static const unsigned int zone_sizes[] =
{
126,
};
static const char *led_names[] =
{
"Key: Escape",
"Key: `",
"Key: Tab",
"Key: Caps Lock",
"Key: Left Shift",
"Key: Left Control",
// Skip index 6
"Key: 1",
"Key: Q",
"Key: A",
"Key: Z",
"Key: Left Windows",
"Key: F1",
"Key: 2",
"Key: W",
"Key: S",
"Key: X",
"Key: Left Alt",
"Key: F2",
"Key: 3",
"Key: E",
"Key: D",
"Key: C",
// Skip index 23
"Key: F3",
"Key: 4",
"Key: R",
"Key: F",
"Key: V",
// Skip index 29
"Key: F4",
"Key: 5",
"Key: T",
"Key: G",
"Key: B",
"Key: Space",
"Key: F5",
"Key: 6",
"Key: Y",
"Key: H",
"Key: N",
// Skip index 41
"Key: F6",
"Key: 7",
"Key: U",
"Key: J",
"Key: M",
// Skip index 47
"Key: F7",
"Key: 8",
"Key: I",
"Key: K",
"Key: ,",
"Key: Right Alt",
"Key: F8",
"Key: 9",
"Key: O",
"Key: L",
"Key: .",
// Skip index 59
"Key: Menu",
"Key: F9",
"Key: 0",
"Key: P",
"Key: ;",
"Key: /",
"Key: Right Windows",
"Key: F10",
"Key: -",
"Key: [",
// Skip index 70
// Skip index 71
"Key: '",
"Key: F11",
"Key: =",
// Skip index 75
// Skip index 76
"Key: ]",
"Key: F12",
"Key: Backspace",
"Key: \\ (ANSI)",
"Key: Enter (ISO)",
"Key: Right Shift",
"Key: Right Control",
"Key: Print Screen",
"Key: Insert",
"Key: Delete",
// Skip index 87
// Skip index 88
"Key: Left Arrow",
"Key: Scroll Lock",
"Key: Home",
"Key: End",
// Skip index 93
"Key: Up Arrow",
"Key: Down Arrow",
"Key: Pause/Break",
"Key: Page Up",
"Key: Page Down",
// Skip index 99
// Skip index 100
"Key: Right Arrow",
// Skip index 102
"Key: Num Lock",
"Key: Number Pad 7",
"Key: Number Pad 4",
"Key: Number Pad 1",
"Key: Number Pad 0",
// Skip index 108
"Key: Number Pad /",
"Key: Number Pad 8",
"Key: Number Pad 5",
"Key: Number Pad 2",
// Skip index 113
// Skip index 114
// Last multimedia key
"Key: Media Mute",
"Key: Number Pad *",
"Key: Number Pad 9",
"Key: Number Pad 3",
"Key: Number Pad 6",
// Skip index 120
"Key: Number Pad .",
// First multimedia key
"Key: Media Previous",
// Skip index 123
// Skip index 124
"Key: Number Pad -",
"Key: Number Pad +",
// Second multimedia key
"Key: Media Play/Pause",
// Third multimedia key
"Key: Media Next",
"Key: Number Pad Enter",
"RGB Strip 1",
"RGB Strip 2",
"RGB Strip 3",
"RGB Strip 4",
"RGB Strip 5",
"RGB Strip 6",
"RGB Strip 7",
"RGB Strip 8",
"RGB Strip 9",
"RGB Strip 10",
"RGB Strip 11",
"RGB Strip 12",
"RGB Strip 13",
"RGB Strip 14",
"RGB Strip 15",
"RGB Strip 16",
"RGB Strip 17",
"RGB Strip 18",
};
RGBController_HyperXAlloyElite2::RGBController_HyperXAlloyElite2(HyperXAlloyElite2Controller* hyperx_ptr)
{
hyperx = hyperx_ptr;
name = "HyperX Alloy Elite 2 Keyboard Device";
vendor = "HyperX";
type = DEVICE_TYPE_KEYBOARD;
description = "HyperX Alloy Elite 2 Keyboard Device";
location = hyperx->GetDeviceLocation();
serial = hyperx->GetSerialString();
mode Direct;
Direct.name = "Direct";
Direct.value = 0xFFFF;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
/*-----------------------------------------------------*\
| The Corsair Lighting Node Pro requires a packet within|
| 20 seconds of sending the lighting change in order |
| to not revert back into rainbow mode. Start a thread |
| to continuously send a keepalive packet every 5s |
\*-----------------------------------------------------*/
keepalive_thread_run = true;
keepalive_thread = new std::thread(&RGBController_HyperXAlloyElite2::KeepaliveThreadFunction, this);
}
RGBController_HyperXAlloyElite2::~RGBController_HyperXAlloyElite2()
{
keepalive_thread_run = false;
keepalive_thread->join();
delete keepalive_thread;
/*---------------------------------------------------------*\
| Delete the matrix map |
\*---------------------------------------------------------*/
for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++)
{
if(zones[zone_index].matrix_map != nullptr)
{
delete zones[zone_index].matrix_map;
}
}
delete hyperx;
}
void RGBController_HyperXAlloyElite2::SetupZones()
{
/*---------------------------------------------------------*\
| Set up zones |
\*---------------------------------------------------------*/
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];
new_zone.leds_min = zone_sizes[zone_idx];
new_zone.leds_max = zone_sizes[zone_idx];
new_zone.leds_count = zone_sizes[zone_idx];
if(zone_types[zone_idx] == ZONE_TYPE_MATRIX)
{
new_zone.matrix_map = new matrix_map_type;
new_zone.matrix_map->height = 8;
new_zone.matrix_map->width = 22;
new_zone.matrix_map->map = (unsigned int *)&matrix_map;
}
else
{
new_zone.matrix_map = nullptr;
}
zones.push_back(new_zone);
total_led_count += zone_sizes[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);
}
SetupColors();
}
void RGBController_HyperXAlloyElite2::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_HyperXAlloyElite2::DeviceUpdateLEDs()
{
last_update_time = std::chrono::steady_clock::now();
if(active_mode == 0)
{
hyperx->SetLEDsDirect(colors);
}
}
void RGBController_HyperXAlloyElite2::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_HyperXAlloyElite2::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_HyperXAlloyElite2::SetCustomMode()
{
active_mode = 0;
}
void RGBController_HyperXAlloyElite2::DeviceUpdateMode()
{
}
void RGBController_HyperXAlloyElite2::KeepaliveThreadFunction()
{
while(keepalive_thread_run)
{
if(active_mode == 0)
{
if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(1000))
{
DeviceUpdateLEDs();
}
}
std::this_thread::sleep_for(50ms);
}
}

View file

@ -0,0 +1,42 @@
/*-----------------------------------------*\
| RGBController_HyperXAlloyElite2.h |
| |
| Generic RGB Interface for HyperX Alloy |
| Elite2 RGB Keyboard |
| |
| KundaPanda (vojdo) 02/04/2021 |
\*-----------------------------------------*/
#pragma once
#include <atomic>
#include <chrono>
#include <thread>
#include "RGBController.h"
#include "HyperXAlloyElite2Controller.h"
class RGBController_HyperXAlloyElite2 : public RGBController
{
public:
RGBController_HyperXAlloyElite2(HyperXAlloyElite2Controller* hyperx_ptr);
~RGBController_HyperXAlloyElite2();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void SetCustomMode();
void DeviceUpdateMode();
void KeepaliveThreadFunction();
private:
HyperXAlloyElite2Controller* hyperx;
std::atomic<bool> keepalive_thread_run;
std::thread* keepalive_thread;
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
};