Files
OpenRGB/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.cpp

240 lines
9.4 KiB
C++

/*---------------------------------------------------------*\
| RGBController_QMKKeychron.cpp |
| |
| RGBController for Keychron QMK-based keyboards |
| |
| Amadej Kastelic 21 Jun 2026 |
| Adam Honse <calcprogrammer1@gmail.com> 22 Jun 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include "hsv.h"
#include "RGBController_QMKKeychron.h"
#include "QMKKeycodes.h"
#include "QMKKeychronController.h"
/**------------------------------------------------------------------*\
@name QMK Keychron
@category Keyboard
@type USB
@save :white_check_mark:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectQMKKeychronController
@comment
\*-------------------------------------------------------------------*/
typedef struct
{
std::string name;
int value;
int flags;
} kc_effect;
static const kc_effect kc_effects[] =
{
{ "Direct", 0xFFFF, MODE_FLAG_HAS_PER_LED_COLOR },
{ "Solid Color", 1, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE },
{ "Breathing", 2, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE },
{ "Band Spiral", 3, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE },
{ "Cycle All", 4, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Cycle Left Right", 5, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Cycle Up Down", 6, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Rainbow Moving Chevron", 7, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Cycle Out In", 8, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Cycle Out In Dual", 9, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Cycle Pinwheel", 10, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Cycle Spiral", 11, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Dual Beacon", 12, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Rainbow Beacon", 13, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Jellybean Raindrops", 14, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Pixel Rain", 15, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Typing Heatmap", 16, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Digital Rain", 17, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE },
{ "Solid Reactive Simple", 18, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE },
{ "Solid Reactive Multiwide", 19, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE },
{ "Solid Reactive Multinexus", 20, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE },
{ "Splash", 21, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE },
{ "Solid Splash", 22, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE },
};
RGBController_QMKKeychron::RGBController_QMKKeychron(QMKKeychronController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetName();
vendor = controller->GetVendor();
type = DEVICE_TYPE_KEYBOARD;
description = "QMK Keychron Device";
location = controller->GetLocation();
serial = controller->GetSerial();
version = controller->GetVersion();
for(const kc_effect& effect : kc_effects)
{
mode m;
m.name = effect.name;
m.value = effect.value;
m.flags = effect.flags;
if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR)
{
m.color_mode = MODE_COLORS_MODE_SPECIFIC;
m.colors_min = 1;
m.colors_max = 1;
m.colors.resize(1);
}
else if(m.flags & MODE_FLAG_HAS_PER_LED_COLOR)
{
m.color_mode = MODE_COLORS_PER_LED;
m.colors_min = 0;
m.colors_max = 0;
m.colors.resize(0);
}
else
{
m.color_mode = MODE_COLORS_NONE;
m.colors_min = 0;
m.colors_max = 0;
m.colors.resize(0);
}
if(m.flags & MODE_FLAG_HAS_SPEED)
{
m.speed_min = KEYCHRON_QHE_MIN_SPEED;
m.speed_max = KEYCHRON_QHE_MAX_SPEED;
m.speed = KEYCHRON_QHE_MAX_SPEED / 2;
}
if(m.flags & MODE_FLAG_HAS_BRIGHTNESS)
{
m.brightness_min = KEYCHRON_QHE_MIN_BRIGHTNESS;
m.brightness_max = KEYCHRON_QHE_MAX_BRIGHTNESS;
m.brightness = KEYCHRON_QHE_MAX_BRIGHTNESS;
}
modes.push_back(m);
}
SetupZones();
}
RGBController_QMKKeychron::~RGBController_QMKKeychron()
{
delete controller;
}
void RGBController_QMKKeychron::SetupZones()
{
/*-----------------------------------------------------*\
| Build matrix map |
\*-----------------------------------------------------*/
unsigned char max_col = 0;
unsigned char max_row = 0;
for(unsigned short led_index = 0; led_index < controller->GetLEDCount(); led_index++)
{
kc_led_info info = controller->GetLEDInfo(led_index);
if(info.col > max_col)
{
max_col = info.col;
}
if(info.row > max_row)
{
max_row = info.row;
}
}
unsigned char height = max_row + 1;
unsigned char width = max_col + 1;
unsigned int* matrix_map = new unsigned int[width * height];
memset(matrix_map, 0xFF, (sizeof(unsigned int) * (width * height)));
for(unsigned short led_index = 0; led_index < controller->GetLEDCount(); led_index++)
{
kc_led_info info = controller->GetLEDInfo(led_index);
matrix_map[(width * info.row) + info.col] = (unsigned int)led_index;
}
/*-----------------------------------------------------*\
| Create keyboard zone |
\*-----------------------------------------------------*/
zone keyboard;
keyboard.name = "Keyboard";
keyboard.type = ZONE_TYPE_MATRIX;
keyboard.leds_min = controller->GetLEDCount();
keyboard.leds_max = controller->GetLEDCount();
keyboard.leds_count = controller->GetLEDCount();
keyboard.matrix_map = new matrix_map_type;
keyboard.matrix_map->height = height;
keyboard.matrix_map->width = width;
keyboard.matrix_map->map = matrix_map;
zones.push_back(keyboard);
/*-----------------------------------------------------*\
| Create keyboard LEDs |
\*-----------------------------------------------------*/
for(unsigned short led_idx = 0; led_idx < controller->GetLEDCount(); led_idx++)
{
led new_led;
new_led.name = qmk_keynames[controller->GetKeycode(led_idx)];
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_QMKKeychron::ResizeZone(int /*zone*/, int /*new_size*/)
{
}
void RGBController_QMKKeychron::DeviceUpdateLEDs()
{
controller->SendLEDs((unsigned short)colors.size(), colors.data());
}
void RGBController_QMKKeychron::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_QMKKeychron::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_QMKKeychron::DeviceUpdateMode()
{
unsigned char hue = 0;
unsigned char sat = 255;
unsigned char val = modes[active_mode].brightness;
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
{
hsv_t hsv_color;
rgb2hsv(modes[active_mode].colors[0], &hsv_color);
hue = (unsigned char)((float)hsv_color.hue * (256.0f / 360.0f));
sat = hsv_color.saturation;
val = hsv_color.value;
}
controller->SetMode(modes[active_mode].value, modes[active_mode].speed, hue, sat, val);
}
void RGBController_QMKKeychron::DeviceSaveMode()
{
controller->SaveMode();
}