Add Dark Project KD104A support to the Keyrox Classic driver

This commit is contained in:
Ilya Miller
2026-07-24 18:53:06 +00:00
committed by Adam Honse
parent 9fd79b1ad2
commit 8a831cef04
8 changed files with 533 additions and 323 deletions

View File

@@ -12,22 +12,18 @@
#include <hidapi.h>
#include "DetectionManager.h"
#include "RedSquareKeyroxController.h"
#include "RedSquareKeyroxTKLClassicController.h"
#include "RGBController_RedSquareKeyrox.h"
#include "RGBController_RedSquareKeyroxTKLClassic.h"
/*---------------------------------------------------------*\
| Red Square vendor ID |
\*---------------------------------------------------------*/
#define RED_SQUARE_VID 0x1A2C
#define RED_SQUARE_KEYROX_TKL_CLASSIC_VID 0x0416
/*---------------------------------------------------------*\
| Red Square product ID |
\*---------------------------------------------------------*/
#define RED_SQUARE_KEYROX_TKL_PID 0x1511
#define RED_SQUARE_KEYROX_TKL_V2_PID 0x2511
#define RED_SQUARE_KEYROX_TKL_CLASSIC_PID 0xC345
DetectedControllers DetectRedSquareKeyroxTKL(hid_device_info* info, const std::string& name)
{
@@ -47,24 +43,5 @@ DetectedControllers DetectRedSquareKeyroxTKL(hid_device_info* info, const std::s
return(detected_controllers);
}
DetectedControllers DetectRedSquareKeyroxTKLClassic(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
RedSquareKeyroxTKLClassicController* controller = new RedSquareKeyroxTKLClassicController(dev, *info, name);
RGBController_RedSquareKeyroxTKLClassic* rgb_controller = new RGBController_RedSquareKeyroxTKLClassic(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_IPU("Red Square Keyrox TKL", DetectRedSquareKeyroxTKL, RED_SQUARE_VID, RED_SQUARE_KEYROX_TKL_PID, 3, 0xFF00, 2);
REGISTER_HID_DETECTOR_IPU("Red Square Keyrox TKL V2", DetectRedSquareKeyroxTKL, RED_SQUARE_VID, RED_SQUARE_KEYROX_TKL_V2_PID, 3, 0xFF00, 2);
REGISTER_HID_DETECTOR_I( "Red Square Keyrox TKL Classic", DetectRedSquareKeyroxTKLClassic, RED_SQUARE_KEYROX_TKL_CLASSIC_VID, RED_SQUARE_KEYROX_TKL_CLASSIC_PID, 2);

View File

@@ -1,169 +0,0 @@
/*---------------------------------------------------------*\
| RedSquareKeyroxTKLClassicController.cpp |
| |
| Driver for Red Square Keyrox TKL Classic |
| |
| vlack 03 May 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "LogManager.h"
#include "RedSquareKeyroxTKLClassicController.h"
using namespace std::chrono_literals;
RedSquareKeyroxTKLClassicController::RedSquareKeyroxTKLClassicController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name)
{
dev = dev_handle;
location = info.path;
name = dev_name;
}
RedSquareKeyroxTKLClassicController::~RedSquareKeyroxTKLClassicController()
{
hid_close(dev);
}
std::string RedSquareKeyroxTKLClassicController::GetDeviceLocation()
{
return("HID: " + location);
}
std::string RedSquareKeyroxTKLClassicController::GetNameString()
{
return(name);
}
std::string RedSquareKeyroxTKLClassicController::GetSerialString()
{
wchar_t serial_wchar[128];
hid_get_serial_number_string(dev, serial_wchar, 128);
std::wstring serial_wstring(serial_wchar);
std::string serial_string;
std::transform(serial_wstring.begin(), serial_wstring.end(), std::back_inserter(serial_string), [] (wchar_t i)
{
return (char)i;
});
return serial_string;
}
int RedSquareKeyroxTKLClassicController::GetDirection(int direction)
{
switch(direction)
{
case MODE_DIRECTION_LEFT:
return 0x00;
case MODE_DIRECTION_RIGHT:
return 0x01;
case MODE_DIRECTION_UP:
return 0x02;
case MODE_DIRECTION_DOWN:
return 0x03;
case MODE_DIRECTION_HORIZONTAL: // actually direction out
return 0x04;
case MODE_DIRECTION_VERTICAL: // actually direction in
return 0x05;
default:
return 0x00;
}
}
int RedSquareKeyroxTKLClassicController::GetDirectionRound(int direction)
{
switch(direction)
{
case MODE_DIRECTION_LEFT: // actually anticlockwise
return 0x07;
case MODE_DIRECTION_RIGHT: // actually clockwise
return 0x06;
default:
return 0x00;
}
}
void RedSquareKeyroxTKLClassicController::SetMode(mode m)
{
/*---------------------------------------------*\
| Mode set command |
\*---------------------------------------------*/
unsigned char usb_buf[CLASSIC_PACKET_DATA_LENGTH];
memset(usb_buf, 0x00, CLASSIC_PACKET_DATA_LENGTH);
usb_buf[0] = 0x01;
usb_buf[1] = 0x07;
usb_buf[6] = m.value;
usb_buf[7] = m.brightness; // brightness
usb_buf[8] = m.speed; // speed
if(m.colors_max == 1 || m.colors_max == 2)
{
usb_buf[9] = RGBGetRValue(m.colors[0]); // front R
usb_buf[10] = RGBGetGValue(m.colors[0]); // front G
usb_buf[11] = RGBGetBValue(m.colors[0]); // front B
}
if(m.colors_max == 2 && m.color_mode != MODE_COLORS_RANDOM)
{
usb_buf[12] = RGBGetRValue(m.colors[1]); // back R
usb_buf[13] = RGBGetGValue(m.colors[1]); // back G
usb_buf[14] = RGBGetBValue(m.colors[1]); // back B
}
if(m.value == CLASSIC_RADAR_MODE_VALUE || m.value == CLASSIC_RUNNING_LINE_MODE_VALUE)
{
usb_buf[15] = GetDirectionRound(m.direction);
}
else
{
usb_buf[15] = GetDirection(m.direction);
}
usb_buf[16] = m.color_mode == MODE_COLORS_RANDOM;
hid_write(dev, usb_buf, CLASSIC_PACKET_DATA_LENGTH);
// sleep is necessary
std::this_thread::sleep_for(10ms);
}
void RedSquareKeyroxTKLClassicController::SetLEDsData(std::vector<RGBColor> colors, std::vector<led> leds)
{
/*---------------------------------------------*\
| LEDs data set command |
\*---------------------------------------------*/
unsigned char usb_buf[CLASSIC_PACKET_DATA_LENGTH * 8];
memset(usb_buf, 0x00, CLASSIC_PACKET_DATA_LENGTH * 8);
for(unsigned int i = 0; i < colors.size(); i++)
{
int offset = leds[i].value;
usb_buf[offset - 1] = RGBGetRValue(colors[i]);
usb_buf[offset] = RGBGetGValue(colors[i]);
usb_buf[offset + 1] = RGBGetBValue(colors[i]);
}
for(unsigned int i = 0; i < 8; i++)
{
unsigned char packet[CLASSIC_PACKET_DATA_LENGTH];
memset(packet, 0x00, CLASSIC_PACKET_DATA_LENGTH);
packet[0] = 0x01;
packet[1] = 0x0F;
packet[4] = i; // package number
packet[5] = i == 7 ? 0x12 : 0x36; // package size
for (int x = 6; x < CLASSIC_PACKET_DATA_LENGTH; x++)
{
packet[x] = usb_buf[CLASSIC_PACKET_DATA_LENGTH * i + x];
}
hid_write(dev, packet, CLASSIC_PACKET_DATA_LENGTH);
}
// sleep is necessary
std::this_thread::sleep_for(10ms);
}

View File

@@ -1,78 +0,0 @@
/*---------------------------------------------------------*\
| RedSquareKeyroxTKLClassicController.h |
| |
| Driver for Red Square Keyrox TKL Classic |
| |
| vlack 03 May 2023 |
| |
| 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"
#define CLASSIC_PACKET_DATA_LENGTH 64
/*---------------------------------------*\
| Modes |
\*---------------------------------------*/
enum
{
CLASSIC_CONST_MODE_VALUE = 0x00, // static
CLASSIC_BREATHE_MODE_VALUE = 0x01, // breath
CLASSIC_WAVE_MODE_VALUE = 0x02, // wave
CLASSIC_FADE_MODE_VALUE = 0x03, // neon
CLASSIC_RADAR_MODE_VALUE = 0x04, // radar
CLASSIC_STAR_MODE_VALUE = 0x06, // интерактив
CLASSIC_LINE_MODE_VALUE = 0x07, // сияние
CLASSIC_RIPPLE_MODE_VALUE = 0x08, // рябь интерактив
CLASSIC_STARS_MODE_VALUE = 0x09, // мерцание
CLASSIC_CUSTOM_MODE_VALUE = 0x0A,
CLASSIC_CROSS_MODE_VALUE = 0x0B, // скрещивание
CLASSIC_WTF_MODE_VALUE = 0x0C, // быстрый отклик
CLASSIC_RIPPLE_RANDOM_MODE_VALUE = 0x0E, // рябь
CLASSIC_RUNNING_LINE_MODE_VALUE = 0x0F, // бегущая строка
CLASSIC_FIREWORK_MODE_VALUE = 0x10, // firework
};
/*-----------------------------*\
| Other settings |
\*-----------------------------*/
enum
{
CLASSIC_KEYROX_BRIGHTNESS_MIN = 0x00,
CLASSIC_KEYROX_BRIGHTNESS_MAX = 0x04,
CLASSIC_KEYROX_SPEED_MIN = 0x00,
CLASSIC_KEYROX_SPEED_MAX = 0x04,
};
class RedSquareKeyroxTKLClassicController
{
public:
RedSquareKeyroxTKLClassicController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name);
~RedSquareKeyroxTKLClassicController();
std::string GetDeviceLocation();
std::string GetNameString();
std::string GetSerialString();
int GetDirection(int direction);
int GetDirectionRound(int direction);
void SetMode(mode m);
void SetLEDsData(std::vector<RGBColor> colors, std::vector<led> leds);
protected:
hid_device* dev;
private:
std::string location;
std::string name;
std::string serial_number;
std::vector<unsigned int> led_sequence_positions;
};

View File

@@ -1,7 +1,7 @@
/*---------------------------------------------------------*\
| RGBController_RedSquareKeyroxTKLClassic.cpp |
| RGBController_WitmodKeyboard.cpp |
| |
| RGBController for Red Square Keyrox TKL Classic |
| RGBController for Witmod keyboards |
| |
| vlack 03 May 2023 |
| |
@@ -9,17 +9,21 @@
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_RedSquareKeyroxTKLClassic.h"
#include "RGBController_WitmodKeyboard.h"
/**------------------------------------------------------------------*\
@name Keyrox
@name Witmod Keyboard
@category Keyboard
@type USB
@save :robot:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectRedSquareKeyroxTKLClassic
@comment Also named Dark Project KD87a
@detectors DetectWitmodKeyboard
@comment Witmod platform. The GK8110 (87 key) and GK8120 (104 key)
boards are resold under several brands, including Dark Project KD87A
and KD104A and the Red Square Keyrox TKL Classic. The sizes share
the same USB IDs and are told apart by the model reported in the
device info string.
\*-------------------------------------------------------------------*/
typedef struct
@@ -27,12 +31,12 @@ typedef struct
std::string name;
int value;
int flags;
} keyrox_effect;
} witmod_effect;
/*--------------------*\
| Keyrox TKL Classic |
| GK8110 (87 keys) |
\*--------------------*/
layout_values keyrox_tkl_offset_values =
layout_values gk8110_offset_values =
{
{
/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */
@@ -53,90 +57,115 @@ layout_values keyrox_tkl_offset_values =
}
};
RGBController_RedSquareKeyroxTKLClassic::RGBController_RedSquareKeyroxTKLClassic(RedSquareKeyroxTKLClassicController* controller_ptr)
/*--------------------*\
| GK8120 (104 keys) |
\*--------------------*/
layout_values gk8120_offset_values =
{
{
/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */
7, 13, 16, 19, 22, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58,
/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC INS HOME PGUP NLCK NUM/ NUM* NUM- */
83, 86, 89, 92, 95, 98, 101, 104, 107, 110, 113, 116, 119, 135, 138, 141, 144, 147, 150, 153, 156,
/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NUM7 NUM8 NUM9 NUM+ */
159, 162, 165, 168, 171, 174, 177, 180, 183, 186, 199, 202, 205, 211, 214, 217, 220, 223, 226, 229, 232,
/* CPLK A S D F G H J K L ; " # ENTR NUM4 NUM5 NUM6 */
235, 241, 244, 247, 250, 263, 266, 269, 272, 275, 278, 281, 284, 287, 299, 302, 305,
/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NUM1 NUM2 NUM3 NENT */
311, 314, 327, 330, 333, 336, 339, 342, 345, 348, 351, 354, 363, 369, 375, 378, 391, 394,
/* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR NUM0 NUM. */
397, 400, 403, 415, 427, 430, 433, 436, 442, 455, 458, 461, 467
},
{
/* Add more regional layout fixes here */
}
};
RGBController_WitmodKeyboard::RGBController_WitmodKeyboard(WitmodKeyboardController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "Red Square";
vendor = controller->GetVendorString();
type = DEVICE_TYPE_KEYBOARD;
description = "Red Square Keyrox TKL Classic Device";
description = "Witmod Keyboard Device";
version = controller->GetVersionString();
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
int BASE_EFFECT_FLAGS = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
const int EFFECTS_COUNT = 14;
keyrox_effect keyrox_effects[EFFECTS_COUNT] =
witmod_effect witmod_effects[EFFECTS_COUNT] =
{
{
"Static",
CLASSIC_CONST_MODE_VALUE,
WITMOD_CONST_MODE_VALUE,
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR
},
{
"Direct",
CLASSIC_CUSTOM_MODE_VALUE,
WITMOD_CUSTOM_MODE_VALUE,
MODE_FLAG_HAS_PER_LED_COLOR
},
{
"Wave",
CLASSIC_WAVE_MODE_VALUE,
WITMOD_WAVE_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_DIRECTION_HV
},
{
"Breathing",
CLASSIC_FADE_MODE_VALUE,
WITMOD_FADE_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED
},
{
"Radar",
CLASSIC_RADAR_MODE_VALUE,
WITMOD_RADAR_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR // round animation
},
{
"Star (Interactive)",
CLASSIC_STAR_MODE_VALUE,
WITMOD_STAR_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED
},
{
"Line (Interactive)",
CLASSIC_LINE_MODE_VALUE,
WITMOD_LINE_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_HV
},
{
"Ripple (Interactive)",
CLASSIC_RIPPLE_MODE_VALUE,
WITMOD_RIPPLE_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED
},
{
"Stars",
CLASSIC_STARS_MODE_VALUE,
WITMOD_STARS_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED
},
{
"Cross (Interactive)",
CLASSIC_CROSS_MODE_VALUE,
WITMOD_CROSS_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED
},
{
"Horizontal bars (Interactive)",
CLASSIC_WTF_MODE_VALUE,
WITMOD_WTF_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_DIRECTION_UD
},
{
"Ripple random",
CLASSIC_RIPPLE_RANDOM_MODE_VALUE,
WITMOD_RIPPLE_RANDOM_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED
},
{
"Running line",
CLASSIC_RUNNING_LINE_MODE_VALUE,
WITMOD_RUNNING_LINE_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR // round direction
},
{
"Fireworks (Interactive)",
CLASSIC_FIREWORK_MODE_VALUE,
WITMOD_FIREWORK_MODE_VALUE,
BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED
},
};
@@ -144,11 +173,11 @@ RGBController_RedSquareKeyroxTKLClassic::RGBController_RedSquareKeyroxTKLClassic
for(int i = 0; i < EFFECTS_COUNT; i++)
{
mode m;
m.name = keyrox_effects[i].name;
m.value = keyrox_effects[i].value;
m.flags = keyrox_effects[i].flags | MODE_FLAG_HAS_BRIGHTNESS;
m.name = witmod_effects[i].name;
m.value = witmod_effects[i].value;
m.flags = witmod_effects[i].flags | MODE_FLAG_HAS_BRIGHTNESS;
if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR && m.value != CLASSIC_CONST_MODE_VALUE)
if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR && m.value != WITMOD_CONST_MODE_VALUE)
{
// background and foreground
m.color_mode = MODE_COLORS_MODE_SPECIFIC;
@@ -176,15 +205,15 @@ RGBController_RedSquareKeyroxTKLClassic::RGBController_RedSquareKeyroxTKLClassic
if(m.flags & MODE_FLAG_HAS_SPEED)
{
m.speed_min = CLASSIC_KEYROX_SPEED_MIN;
m.speed_max = CLASSIC_KEYROX_SPEED_MAX;
m.speed = (CLASSIC_KEYROX_SPEED_MAX - CLASSIC_KEYROX_SPEED_MIN) / 2;
m.speed_min = WITMOD_SPEED_MIN;
m.speed_max = WITMOD_SPEED_MAX;
m.speed = (WITMOD_SPEED_MAX - WITMOD_SPEED_MIN) / 2;
}
if(m.flags & MODE_FLAG_HAS_BRIGHTNESS)
{
m.brightness_min = CLASSIC_KEYROX_BRIGHTNESS_MIN;
m.brightness_max = CLASSIC_KEYROX_BRIGHTNESS_MAX;
m.brightness_min = WITMOD_BRIGHTNESS_MIN;
m.brightness_max = WITMOD_BRIGHTNESS_MAX;
m.brightness = m.brightness_max;
}
@@ -194,16 +223,32 @@ RGBController_RedSquareKeyroxTKLClassic::RGBController_RedSquareKeyroxTKLClassic
SetupZones();
}
RGBController_RedSquareKeyroxTKLClassic::~RGBController_RedSquareKeyroxTKLClassic()
RGBController_WitmodKeyboard::~RGBController_WitmodKeyboard()
{
Shutdown();
delete controller;
}
void RGBController_RedSquareKeyroxTKLClassic::SetupZones()
void RGBController_WitmodKeyboard::SetupZones()
{
KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ANSI_QWERTY, KEYBOARD_SIZE_TKL, keyrox_tkl_offset_values);
/*---------------------------------------------------------*\
| Pick the layout for the size the controller reported. |
| The number pad sits in the four matrix columns to the |
| right of the GK8110 keys, so the map has to be built |
| wider or those keys are dropped from it. |
\*---------------------------------------------------------*/
KEYBOARD_SIZE keyboard_size = controller->GetKeyboardSize();
const layout_values* offset_values = &gk8110_offset_values;
unsigned char matrix_width = GK8110_WIDTH;
if(keyboard_size == KEYBOARD_SIZE_FULL)
{
offset_values = &gk8120_offset_values;
matrix_width = GK8120_WIDTH;
}
KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ANSI_QWERTY, keyboard_size, *offset_values);
/*---------------------------------------------------------*\
| Create the keyboard zone usiung Keyboard Layout Manager |
@@ -214,7 +259,7 @@ void RGBController_RedSquareKeyroxTKLClassic::SetupZones()
new_zone.leds_count = new_kb.GetKeyCount();
new_zone.leds_min = new_zone.leds_count;
new_zone.leds_max = new_zone.leds_count;
new_zone.matrix_map = new_kb.GetKeyMap(KEYBOARD_MAP_FILL_TYPE_COUNT, KEYROX_TKL_CLASSIC_HEIGHT, KEYROX_TKL_CLASSIC_WIDTH);
new_zone.matrix_map = new_kb.GetKeyMap(KEYBOARD_MAP_FILL_TYPE_COUNT, WITMOD_MATRIX_HEIGHT, matrix_width);
/*---------------------------------------------------------*\
| Create LEDs for the Matrix zone |
@@ -234,22 +279,22 @@ void RGBController_RedSquareKeyroxTKLClassic::SetupZones()
SetupColors();
}
void RGBController_RedSquareKeyroxTKLClassic::DeviceUpdateLEDs()
void RGBController_WitmodKeyboard::DeviceUpdateLEDs()
{
controller->SetLEDsData(colors, leds);
}
void RGBController_RedSquareKeyroxTKLClassic::DeviceUpdateZoneLEDs(int /*zone*/)
void RGBController_WitmodKeyboard::DeviceUpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_RedSquareKeyroxTKLClassic::DeviceUpdateSingleLED(int /*led*/)
void RGBController_WitmodKeyboard::DeviceUpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_RedSquareKeyroxTKLClassic::DeviceUpdateMode()
void RGBController_WitmodKeyboard::DeviceUpdateMode()
{
controller->SetMode(modes[active_mode]);
}

View File

@@ -1,7 +1,7 @@
/*---------------------------------------------------------*\
| RGBController_RedSquareKeyroxTKLClassic.h |
| RGBController_WitmodKeyboard.h |
| |
| RGBController for Red Square Keyrox TKL Classic |
| RGBController for Witmod keyboards |
| |
| vlack 03 May 2023 |
| |
@@ -14,16 +14,22 @@
#include "RGBController.h"
#include "RGBControllerKeyNames.h"
#include "KeyboardLayoutManager.h"
#include "RedSquareKeyroxTKLClassicController.h"
#include "WitmodKeyboardController.h"
#define KEYROX_TKL_CLASSIC_WIDTH 17
#define KEYROX_TKL_CLASSIC_HEIGHT 6
/*---------------------------------------------------------*\
| Both sizes share the same six row hardware matrix. The |
| GK8120 adds the number pad in the four columns to the |
| right of the GK8110 keys. |
\*---------------------------------------------------------*/
#define GK8110_WIDTH 17
#define GK8120_WIDTH 21
#define WITMOD_MATRIX_HEIGHT 6
class RGBController_RedSquareKeyroxTKLClassic : public RGBController
class RGBController_WitmodKeyboard : public RGBController
{
public:
RGBController_RedSquareKeyroxTKLClassic(RedSquareKeyroxTKLClassicController* controller_ptr);
~RGBController_RedSquareKeyroxTKLClassic();
RGBController_WitmodKeyboard(WitmodKeyboardController* controller_ptr);
~RGBController_WitmodKeyboard();
void SetupZones();
@@ -34,5 +40,5 @@ public:
void DeviceUpdateMode();
private:
RedSquareKeyroxTKLClassicController* controller;
WitmodKeyboardController* controller;
};

View File

@@ -0,0 +1,283 @@
/*---------------------------------------------------------*\
| WitmodKeyboardController.cpp |
| |
| Driver for Witmod keyboards |
| |
| vlack 03 May 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "LogManager.h"
#include "WitmodKeyboardController.h"
using namespace std::chrono_literals;
WitmodKeyboardController::WitmodKeyboardController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name)
{
dev = dev_handle;
location = info.path;
name = dev_name;
vendor = "Witmod";
keyboard_size = KEYBOARD_SIZE_TKL;
ReadDeviceInfo();
}
WitmodKeyboardController::~WitmodKeyboardController()
{
hid_close(dev);
}
std::string WitmodKeyboardController::GetDeviceLocation()
{
return("HID: " + location);
}
std::string WitmodKeyboardController::GetNameString()
{
return(name);
}
std::string WitmodKeyboardController::GetSerialString()
{
wchar_t serial_wchar[128];
hid_get_serial_number_string(dev, serial_wchar, 128);
std::wstring serial_wstring(serial_wchar);
std::string serial_string;
std::transform(serial_wstring.begin(), serial_wstring.end(), std::back_inserter(serial_string), [] (wchar_t i)
{
return (char)i;
});
return serial_string;
}
std::string WitmodKeyboardController::GetVendorString()
{
return(vendor);
}
std::string WitmodKeyboardController::GetVersionString()
{
return(version);
}
KEYBOARD_SIZE WitmodKeyboardController::GetKeyboardSize()
{
return(keyboard_size);
}
/*---------------------------------------------------------*\
| Ask the keyboard which model it is. The boards share |
| their vendor ID, product ID and product string, so the |
| model in the reply to this request is the only way to |
| tell the sizes apart. A board that does not answer, or |
| reports an unknown model, keeps the default TKL layout. |
\*---------------------------------------------------------*/
void WitmodKeyboardController::ReadDeviceInfo()
{
unsigned char usb_buf[WITMOD_PACKET_DATA_LENGTH];
memset(usb_buf, 0x00, WITMOD_PACKET_DATA_LENGTH);
usb_buf[0] = 0x01;
usb_buf[1] = WITMOD_INFO_COMMAND;
hid_write(dev, usb_buf, WITMOD_PACKET_DATA_LENGTH);
for(int retry = 0; retry < WITMOD_INFO_RETRIES; retry++)
{
/*-----------------------------------------*\
| The extra byte keeps the reply string |
| terminated if it fills the packet |
\*-----------------------------------------*/
char reply[WITMOD_PACKET_DATA_LENGTH + 1];
memset(reply, 0x00, WITMOD_PACKET_DATA_LENGTH + 1);
int reply_size = hid_read_timeout(dev, (unsigned char *)reply, WITMOD_PACKET_DATA_LENGTH, WITMOD_INFO_TIMEOUT);
if(reply_size <= 0)
{
continue;
}
/*-----------------------------------------*\
| Effects send reports of their own, so |
| skip anything that is not the answer |
\*-----------------------------------------*/
if((reply[0] != 0x01) || (reply[1] != WITMOD_INFO_COMMAND))
{
continue;
}
/*-----------------------------------------*\
| Step over the command bytes and the |
| padding ahead of the string |
\*-----------------------------------------*/
const char* info = &reply[2];
for(int idx = 2; idx < WITMOD_PACKET_DATA_LENGTH; idx++, info++)
{
if(*info > ' ')
{
break;
}
}
if(*info == '\0')
{
continue;
}
LOG_INFO("[%s] Device info: %s", name.c_str(), info);
const char* version_field = strrchr(info, 'V');
if(version_field != NULL)
{
version = version_field;
}
if(strstr(info, WITMOD_MODEL_GK8120) != NULL)
{
LOG_INFO("[%s] GK8120 full size board, using the 104 key layout", name.c_str());
keyboard_size = KEYBOARD_SIZE_FULL;
name = "Witmod GK8120";
}
else if(strstr(info, WITMOD_MODEL_GK8110) != NULL)
{
LOG_INFO("[%s] GK8110 TKL board, using the 87 key layout", name.c_str());
keyboard_size = KEYBOARD_SIZE_TKL;
name = "Witmod GK8110";
}
else
{
LOG_WARNING("[%s] Unrecognised model, defaulting to the 87 key layout", name.c_str());
}
return;
}
LOG_WARNING("[%s] Device info request went unanswered, defaulting to the 87 key layout", name.c_str());
}
int WitmodKeyboardController::GetDirection(int direction)
{
switch(direction)
{
case MODE_DIRECTION_LEFT:
return 0x00;
case MODE_DIRECTION_RIGHT:
return 0x01;
case MODE_DIRECTION_UP:
return 0x02;
case MODE_DIRECTION_DOWN:
return 0x03;
case MODE_DIRECTION_HORIZONTAL: // actually direction out
return 0x04;
case MODE_DIRECTION_VERTICAL: // actually direction in
return 0x05;
default:
return 0x00;
}
}
int WitmodKeyboardController::GetDirectionRound(int direction)
{
switch(direction)
{
case MODE_DIRECTION_LEFT: // actually anticlockwise
return 0x07;
case MODE_DIRECTION_RIGHT: // actually clockwise
return 0x06;
default:
return 0x00;
}
}
void WitmodKeyboardController::SetMode(mode m)
{
/*---------------------------------------------*\
| Mode set command |
\*---------------------------------------------*/
unsigned char usb_buf[WITMOD_PACKET_DATA_LENGTH];
memset(usb_buf, 0x00, WITMOD_PACKET_DATA_LENGTH);
usb_buf[0] = 0x01;
usb_buf[1] = 0x07;
usb_buf[6] = m.value;
usb_buf[7] = m.brightness; // brightness
usb_buf[8] = m.speed; // speed
if(m.colors_max == 1 || m.colors_max == 2)
{
usb_buf[9] = RGBGetRValue(m.colors[0]); // front R
usb_buf[10] = RGBGetGValue(m.colors[0]); // front G
usb_buf[11] = RGBGetBValue(m.colors[0]); // front B
}
if(m.colors_max == 2 && m.color_mode != MODE_COLORS_RANDOM)
{
usb_buf[12] = RGBGetRValue(m.colors[1]); // back R
usb_buf[13] = RGBGetGValue(m.colors[1]); // back G
usb_buf[14] = RGBGetBValue(m.colors[1]); // back B
}
if(m.value == WITMOD_RADAR_MODE_VALUE || m.value == WITMOD_RUNNING_LINE_MODE_VALUE)
{
usb_buf[15] = GetDirectionRound(m.direction);
}
else
{
usb_buf[15] = GetDirection(m.direction);
}
usb_buf[16] = m.color_mode == MODE_COLORS_RANDOM;
hid_write(dev, usb_buf, WITMOD_PACKET_DATA_LENGTH);
// sleep is necessary
std::this_thread::sleep_for(10ms);
}
void WitmodKeyboardController::SetLEDsData(std::vector<RGBColor> colors, std::vector<led> leds)
{
/*---------------------------------------------*\
| LEDs data set command |
\*---------------------------------------------*/
unsigned char usb_buf[WITMOD_PACKET_DATA_LENGTH * 8];
memset(usb_buf, 0x00, WITMOD_PACKET_DATA_LENGTH * 8);
for(unsigned int i = 0; i < colors.size(); i++)
{
int offset = leds[i].value;
usb_buf[offset - 1] = RGBGetRValue(colors[i]);
usb_buf[offset] = RGBGetGValue(colors[i]);
usb_buf[offset + 1] = RGBGetBValue(colors[i]);
}
for(unsigned int i = 0; i < 8; i++)
{
unsigned char packet[WITMOD_PACKET_DATA_LENGTH];
memset(packet, 0x00, WITMOD_PACKET_DATA_LENGTH);
packet[0] = 0x01;
packet[1] = 0x0F;
packet[4] = i; // package number
packet[5] = i == 7 ? 0x12 : 0x36; // package size
for (int x = 6; x < WITMOD_PACKET_DATA_LENGTH; x++)
{
packet[x] = usb_buf[WITMOD_PACKET_DATA_LENGTH * i + x];
}
hid_write(dev, packet, WITMOD_PACKET_DATA_LENGTH);
}
// sleep is necessary
std::this_thread::sleep_for(10ms);
}

View File

@@ -0,0 +1,103 @@
/*---------------------------------------------------------*\
| WitmodKeyboardController.h |
| |
| Driver for Witmod keyboards |
| |
| vlack 03 May 2023 |
| |
| 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 "KeyboardLayoutManager.h"
#define WITMOD_PACKET_DATA_LENGTH 64
/*---------------------------------------------------------*\
| The device info request answers with a string such as |
| |
| 2M252,01,KB,JC,GK8120SKRGB,V1.04.04 |
| |
| where the fifth field is the board model and the last is |
| the firmware version. GK8120 is the full size board and |
| GK8110 the TKL sized one. These are Witmod platform |
| codes; the same boards are resold under several brands. |
\*---------------------------------------------------------*/
#define WITMOD_INFO_COMMAND 0x0D
#define WITMOD_INFO_RETRIES 10
#define WITMOD_INFO_TIMEOUT 150
#define WITMOD_MODEL_GK8110 "GK8110"
#define WITMOD_MODEL_GK8120 "GK8120"
/*---------------------------------------*\
| Modes |
\*---------------------------------------*/
enum
{
WITMOD_CONST_MODE_VALUE = 0x00, // static
WITMOD_BREATHE_MODE_VALUE = 0x01, // breath
WITMOD_WAVE_MODE_VALUE = 0x02, // wave
WITMOD_FADE_MODE_VALUE = 0x03, // neon
WITMOD_RADAR_MODE_VALUE = 0x04, // radar
WITMOD_STAR_MODE_VALUE = 0x06, // интерактив
WITMOD_LINE_MODE_VALUE = 0x07, // сияние
WITMOD_RIPPLE_MODE_VALUE = 0x08, // рябь интерактив
WITMOD_STARS_MODE_VALUE = 0x09, // мерцание
WITMOD_CUSTOM_MODE_VALUE = 0x0A,
WITMOD_CROSS_MODE_VALUE = 0x0B, // скрещивание
WITMOD_WTF_MODE_VALUE = 0x0C, // быстрый отклик
WITMOD_RIPPLE_RANDOM_MODE_VALUE = 0x0E, // рябь
WITMOD_RUNNING_LINE_MODE_VALUE = 0x0F, // бегущая строка
WITMOD_FIREWORK_MODE_VALUE = 0x10, // firework
};
/*-----------------------------*\
| Other settings |
\*-----------------------------*/
enum
{
WITMOD_BRIGHTNESS_MIN = 0x00,
WITMOD_BRIGHTNESS_MAX = 0x04,
WITMOD_SPEED_MIN = 0x00,
WITMOD_SPEED_MAX = 0x04,
};
class WitmodKeyboardController
{
public:
WitmodKeyboardController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name);
~WitmodKeyboardController();
std::string GetDeviceLocation();
std::string GetNameString();
std::string GetSerialString();
std::string GetVendorString();
std::string GetVersionString();
KEYBOARD_SIZE GetKeyboardSize();
int GetDirection(int direction);
int GetDirectionRound(int direction);
void SetMode(mode m);
void SetLEDsData(std::vector<RGBColor> colors, std::vector<led> leds);
protected:
hid_device* dev;
private:
void ReadDeviceInfo();
std::string location;
std::string name;
std::string serial_number;
std::string vendor;
std::string version;
KEYBOARD_SIZE keyboard_size;
std::vector<unsigned int> led_sequence_positions;
};

View File

@@ -0,0 +1,43 @@
/*---------------------------------------------------------*\
| WitmodKeyboardControllerDetect.cpp |
| |
| Detector for Witmod keyboards |
| |
| vlack 03 May 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <hidapi.h>
#include "DetectionManager.h"
#include "WitmodKeyboardController.h"
#include "RGBController_WitmodKeyboard.h"
/*---------------------------------------------------------*\
| Witmod uses the Nuvoton/Winbond USB vendor ID. The |
| GK8110 and GK8120 boards share this VID and PID and are |
| told apart by the model in the device info reply. |
\*---------------------------------------------------------*/
#define WITMOD_VID 0x0416
#define WITMOD_KEYBOARD_PID 0xC345
DetectedControllers DetectWitmodKeyboard(hid_device_info* info, const std::string& name)
{
DetectedControllers detected_controllers;
hid_device* dev;
dev = hid_open_path(info->path);
if(dev)
{
WitmodKeyboardController* controller = new WitmodKeyboardController(dev, *info, name);
RGBController_WitmodKeyboard* rgb_controller = new RGBController_WitmodKeyboard(controller);
detected_controllers.push_back(rgb_controller);
}
return(detected_controllers);
}
REGISTER_HID_DETECTOR_I("Witmod Keyboard", DetectWitmodKeyboard, WITMOD_VID, WITMOD_KEYBOARD_PID, 2);