Implement a matrix patching system in the Keychron QMK controller to apply keyboard-specific patches to correct for misplaced LEDs

This commit is contained in:
Adam Honse
2026-07-12 16:25:07 -05:00
parent 21009cdc45
commit 285a7bce4e
3 changed files with 98 additions and 3 deletions

View File

@@ -19,7 +19,85 @@
using namespace std::chrono_literals;
QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char *path)
/*---------------------------------------------------------*\
| Keychron Patch Types |
| The Keychron protocol does not provide RGB matrix X/Y |
| coordinates, only the position in the wiring matrix. |
| For most Keychron keyboards, the wiring matrix mostly |
| matches the physical position, but there are often a |
| few misplaced LEDs. Implement a matrix patching system |
| to apply keyboard-specific patches for these misplaced |
| LEDs. |
\*---------------------------------------------------------*/
typedef struct
{
unsigned int led_idx;
unsigned int row;
unsigned int col;
} keychron_patch_entry;
typedef struct
{
unsigned short pid;
unsigned int num_entries;
const keychron_patch_entry* patch;
} keychron_patch;
/*---------------------------------------------------------*\
| Keychron Q2 ANSI Encoder |
\*---------------------------------------------------------*/
#define KEYCHRON_Q2_ANSI_ENCODER_PATCH_ENTRIES_COUNT (sizeof(keychron_q2_ansi_encoder_patch_entries) / sizeof(keychron_patch_entry))
static const keychron_patch_entry keychron_q2_ansi_encoder_patch_entries[] =
{
{ 60, 4, 9 },
{ 61, 4, 10 },
{ 62, 4, 11 },
{ 63, 4, 12 },
{ 54, 3, 12 },
{ 55, 3, 13 },
{ 64, 4, 13 },
};
static const keychron_patch keychron_q2_ansi_encoder_patch =
{
KEYCHRON_Q2_ANSI_ENCODER_PID,
KEYCHRON_Q2_ANSI_ENCODER_PATCH_ENTRIES_COUNT,
keychron_q2_ansi_encoder_patch_entries
};
/*---------------------------------------------------------*\
| Keychron Q6 ANSI Encoder |
\*---------------------------------------------------------*/
#define KEYCHRON_Q6_ULTRA_8K_ANSI_PATCH_ENTRIES_COUNT (sizeof(keychron_q6_ultra_8k_ansi_patch_entries) / sizeof(keychron_patch_entry))
static const keychron_patch_entry keychron_q6_ultra_8k_ansi_patch_entries[] =
{
{ 19, 0, 20 },
{ 40, 1, 20 },
{ 77, 3, 20 },
{ 107, 5, 20 },
};
static const keychron_patch keychron_q6_ultra_8k_ansi_patch =
{
KEYCHRON_Q6_ULTRA_8K_ANSI_PID,
KEYCHRON_Q6_ULTRA_8K_ANSI_PATCH_ENTRIES_COUNT,
keychron_q6_ultra_8k_ansi_patch_entries
};
/*---------------------------------------------------------*\
| List of all Keychron patches |
\*---------------------------------------------------------*/
#define KEYCHRON_PATCH_COUNT (sizeof(keychron_patches) / sizeof(keychron_patch*))
static const keychron_patch* keychron_patches[] =
{
&keychron_q2_ansi_encoder_patch,
&keychron_q6_ultra_8k_ansi_patch,
};
QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char *path, unsigned short dev_pid)
{
/*-----------------------------------------------------*\
| Initialize controller fields |
@@ -27,6 +105,7 @@ QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char
dev = dev_handle;
location = path;
kc_protocol_version = 0;
pid = dev_pid;
supported_features = 0;
via_protocol_version = 0;
@@ -143,6 +222,21 @@ QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char
{
keycodes[led_index] = CmdGetKeycode(0, led_info[led_index].row, led_info[led_index].col);
}
/*-----------------------------------------------------*\
| Apply matrix corrections |
\*-----------------------------------------------------*/
for(unsigned int patch_idx = 0; patch_idx < KEYCHRON_PATCH_COUNT; patch_idx++)
{
if(pid == keychron_patches[patch_idx]->pid)
{
for(unsigned int patch_entry_idx = 0; patch_entry_idx < keychron_patches[patch_idx]->num_entries; patch_entry_idx++)
{
led_info[keychron_patches[patch_idx]->patch[patch_entry_idx].led_idx].row = keychron_patches[patch_idx]->patch[patch_entry_idx].row;
led_info[keychron_patches[patch_idx]->patch[patch_entry_idx].led_idx].col = keychron_patches[patch_idx]->patch[patch_entry_idx].col;
}
}
}
}
QMKKeychronController::~QMKKeychronController()

View File

@@ -390,7 +390,7 @@ enum KeychronVIABacklightValueID
class QMKKeychronController
{
public:
QMKKeychronController(hid_device* dev_handle, const char *path);
QMKKeychronController(hid_device* dev_handle, const char *path, unsigned short dev_pid);
~QMKKeychronController();
std::string GetLocation();
@@ -420,6 +420,7 @@ private:
std::string location;
std::string name;
unsigned short number_leds;
unsigned short pid;
std::string serial;
unsigned short supported_features;
std::string vendor;

View File

@@ -23,7 +23,7 @@ DetectedControllers DetectQMKKeychronControllers(hid_device_info* info, const st
if(dev)
{
QMKKeychronController* controller = new QMKKeychronController(dev, info->path);
QMKKeychronController* controller = new QMKKeychronController(dev, info->path, info->product_id);
if(controller->GetSupported())
{