mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2026-06-24 13:48:45 -04:00
feat: add Keychron Q1 HE keyboard support and restructure Keychron controllers
This commit is contained in:
committed by
Adam Honse
parent
c2cc7c6163
commit
175f3ed338
@@ -0,0 +1,375 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| QMKKeychronController.cpp |
|
||||
| |
|
||||
| Driver for Keychron QMK-based keyboards |
|
||||
| (Q1 HE and other KEYCHRON_RGB-enabled models) |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <string.h>
|
||||
#include "QMKKeychronController.h"
|
||||
#include "StringUtils.h"
|
||||
#include "LogManager.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name)
|
||||
{
|
||||
dev = dev_handle;
|
||||
location = info.path;
|
||||
name = dev_name;
|
||||
}
|
||||
|
||||
QMKKeychronController::~QMKKeychronController()
|
||||
{
|
||||
hid_close(dev);
|
||||
}
|
||||
|
||||
std::string QMKKeychronController::GetDeviceLocation()
|
||||
{
|
||||
return("HID: " + location);
|
||||
}
|
||||
|
||||
std::string QMKKeychronController::GetNameString()
|
||||
{
|
||||
return(name);
|
||||
}
|
||||
|
||||
std::string QMKKeychronController::GetSerialString()
|
||||
{
|
||||
wchar_t serial_string[128];
|
||||
int ret = hid_get_serial_number_string(dev, serial_string, 128);
|
||||
|
||||
if(ret != 0)
|
||||
{
|
||||
return("");
|
||||
}
|
||||
|
||||
return(StringUtils::wstring_to_string(serial_string));
|
||||
}
|
||||
|
||||
void QMKKeychronController::SendPacket(unsigned char* data, size_t len)
|
||||
{
|
||||
unsigned char usb_buf[KEYCHRON_QHE_PACKET_SIZE + 1];
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
usb_buf[0] = 0x00;
|
||||
|
||||
size_t copy_len = len;
|
||||
if(copy_len > KEYCHRON_QHE_PACKET_SIZE)
|
||||
{
|
||||
copy_len = KEYCHRON_QHE_PACKET_SIZE;
|
||||
}
|
||||
|
||||
memcpy(&usb_buf[1], data, copy_len);
|
||||
|
||||
hid_write(dev, usb_buf, KEYCHRON_QHE_PACKET_SIZE + 1);
|
||||
|
||||
std::this_thread::sleep_for(5ms);
|
||||
}
|
||||
|
||||
int QMKKeychronController::ReadPacket(unsigned char* buf, size_t buf_len)
|
||||
{
|
||||
unsigned char usb_buf[KEYCHRON_QHE_PACKET_SIZE + 1];
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
int bytes_read = hid_read_timeout(dev, usb_buf, KEYCHRON_QHE_PACKET_SIZE + 1, KEYCHRON_QHE_HID_READ_TIMEOUT);
|
||||
|
||||
if(bytes_read > 0)
|
||||
{
|
||||
size_t copy_len = (size_t)bytes_read;
|
||||
if(copy_len > buf_len)
|
||||
{
|
||||
copy_len = buf_len;
|
||||
}
|
||||
memcpy(buf, usb_buf, copy_len);
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
unsigned int QMKKeychronController::GetLedCount()
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP;
|
||||
cmd[1] = KEYCHRON_KC_RGB_LED_COUNT;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
if(bytes_read > 3 && response[0] == KEYCHRON_KC_RGB_CMD_GROUP && response[1] == KEYCHRON_KC_RGB_LED_COUNT)
|
||||
{
|
||||
return response[3];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<int> QMKKeychronController::GetLedNumbersByRow(unsigned char row)
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP;
|
||||
cmd[1] = KEYCHRON_KC_RGB_LED_IDX;
|
||||
cmd[2] = row;
|
||||
cmd[3] = 0xFF;
|
||||
cmd[4] = 0xFF;
|
||||
cmd[5] = 0xFF;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
std::vector<int> result;
|
||||
|
||||
if(bytes_read > 3 && response[0] == KEYCHRON_KC_RGB_CMD_GROUP && response[1] == KEYCHRON_KC_RGB_LED_IDX)
|
||||
{
|
||||
for(int i = 3; i < bytes_read; i++)
|
||||
{
|
||||
result.push_back(response[i] == 0xFF ? -1 : response[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::vector<int>> QMKKeychronController::GetAllLedNumbers(unsigned char num_rows)
|
||||
{
|
||||
std::vector<std::vector<int>> all_rows;
|
||||
|
||||
for(unsigned char row = 0; row < num_rows; row++)
|
||||
{
|
||||
std::vector<int> row_data = GetLedNumbersByRow(row);
|
||||
all_rows.push_back(row_data);
|
||||
}
|
||||
|
||||
return all_rows;
|
||||
}
|
||||
|
||||
void QMKKeychronController::SetPerKeyRgbColor(unsigned char start, unsigned char count, const std::vector<unsigned char>& hsv_data)
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP;
|
||||
cmd[1] = KEYCHRON_KC_RGB_PER_KEY_SET_COLOR;
|
||||
cmd[2] = start;
|
||||
cmd[3] = count;
|
||||
|
||||
size_t data_offset = 4;
|
||||
size_t copy_len = hsv_data.size();
|
||||
if(data_offset + copy_len > KEYCHRON_QHE_PACKET_SIZE)
|
||||
{
|
||||
copy_len = KEYCHRON_QHE_PACKET_SIZE - data_offset;
|
||||
}
|
||||
|
||||
memcpy(&cmd[data_offset], hsv_data.data(), copy_len);
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE];
|
||||
ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> QMKKeychronController::GetPerKeyRgbColor(unsigned char start, unsigned char count)
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP;
|
||||
cmd[1] = KEYCHRON_KC_RGB_PER_KEY_GET_COLOR;
|
||||
cmd[2] = start;
|
||||
cmd[3] = count;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
std::vector<unsigned char> result;
|
||||
|
||||
if(bytes_read > 3 && response[0] == KEYCHRON_KC_RGB_CMD_GROUP && response[1] == KEYCHRON_KC_RGB_PER_KEY_GET_COLOR)
|
||||
{
|
||||
size_t color_bytes = (size_t)count * 3;
|
||||
if(3 + color_bytes <= (size_t)bytes_read)
|
||||
{
|
||||
for(size_t i = 3; i < 3 + color_bytes; i++)
|
||||
{
|
||||
result.push_back(response[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QMKKeychronController::SetPerKeyRgbType(unsigned char type)
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP;
|
||||
cmd[1] = KEYCHRON_KC_RGB_PER_KEY_SET_TYPE;
|
||||
cmd[2] = type;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE];
|
||||
ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE);
|
||||
}
|
||||
|
||||
void QMKKeychronController::SetRgbMatrixMode(unsigned char mode)
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_VIA_BACKLIGHT_SET_VALUE;
|
||||
cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX;
|
||||
cmd[2] = KEYCHRON_VIA_BACKLIGHT_EFFECT;
|
||||
cmd[3] = mode;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE];
|
||||
ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE);
|
||||
}
|
||||
|
||||
unsigned char QMKKeychronController::GetRgbMatrixMode()
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_VIA_BACKLIGHT_GET_VALUE;
|
||||
cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX;
|
||||
cmd[2] = KEYCHRON_VIA_BACKLIGHT_EFFECT;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
if(bytes_read > 3)
|
||||
{
|
||||
return response[3];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QMKKeychronController::SetBrightness(unsigned char brightness)
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_VIA_BACKLIGHT_SET_VALUE;
|
||||
cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX;
|
||||
cmd[2] = KEYCHRON_VIA_BACKLIGHT_BRIGHTNESS;
|
||||
cmd[3] = brightness;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
}
|
||||
|
||||
unsigned char QMKKeychronController::GetBrightness()
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_VIA_BACKLIGHT_GET_VALUE;
|
||||
cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX;
|
||||
cmd[2] = KEYCHRON_VIA_BACKLIGHT_BRIGHTNESS;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
if(bytes_read > 3)
|
||||
{
|
||||
return response[3];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QMKKeychronController::SetSpeed(unsigned char speed)
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_VIA_BACKLIGHT_SET_VALUE;
|
||||
cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX;
|
||||
cmd[2] = KEYCHRON_VIA_BACKLIGHT_SPEED;
|
||||
cmd[3] = speed;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
}
|
||||
|
||||
unsigned char QMKKeychronController::GetSpeed()
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_VIA_BACKLIGHT_GET_VALUE;
|
||||
cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX;
|
||||
cmd[2] = KEYCHRON_VIA_BACKLIGHT_SPEED;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
if(bytes_read > 3)
|
||||
{
|
||||
return response[3];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QMKKeychronController::SetColorHSV(unsigned char h, unsigned char s)
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_VIA_BACKLIGHT_SET_VALUE;
|
||||
cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX;
|
||||
cmd[2] = KEYCHRON_VIA_BACKLIGHT_COLOR;
|
||||
cmd[3] = h;
|
||||
cmd[4] = s;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
}
|
||||
|
||||
void QMKKeychronController::SaveLedConf()
|
||||
{
|
||||
unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE];
|
||||
memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP;
|
||||
cmd[1] = KEYCHRON_KC_RGB_SAVE;
|
||||
|
||||
SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE);
|
||||
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE];
|
||||
ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE);
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| QMKKeychronController.h |
|
||||
| |
|
||||
| Driver for Keychron QMK-based keyboards |
|
||||
| (Q1 HE and other KEYCHRON_RGB-enabled models) |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RGBController.h"
|
||||
#include <string>
|
||||
#include <hidapi.h>
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Keychron vendor ID |
|
||||
\*---------------------------------------------------------*/
|
||||
#define KEYCHRON_VID 0x3434
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Product IDs |
|
||||
\*---------------------------------------------------------*/
|
||||
#define KEYCHRON_Q1_HE_PID 0x0B10
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| QMK raw HID usage page/usage |
|
||||
\*---------------------------------------------------------*/
|
||||
#define KEYCHRON_QMK_USAGE_PAGE 0xFF60
|
||||
#define KEYCHRON_QMK_USAGE 0x61
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| HID packet constants |
|
||||
\*---------------------------------------------------------*/
|
||||
#define KEYCHRON_QHE_PACKET_SIZE 32
|
||||
#define KEYCHRON_QHE_HID_READ_TIMEOUT 1000
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| KC_RGB command group (0xA8) |
|
||||
| |
|
||||
| Keychron-specific per-key RGB protocol. |
|
||||
| Matches the firmware enum in |
|
||||
| keyboards/keychron/common/rgb/keychron_rgb.c |
|
||||
\*---------------------------------------------------------*/
|
||||
#define KEYCHRON_KC_RGB_CMD_GROUP 0xA8
|
||||
|
||||
enum KeychronKCRGBCommand
|
||||
{
|
||||
KEYCHRON_KC_RGB_PROTOCOL_VER = 0x01,
|
||||
KEYCHRON_KC_RGB_SAVE = 0x02,
|
||||
KEYCHRON_KC_RGB_GET_INDICATORS = 0x03,
|
||||
KEYCHRON_KC_RGB_SET_INDICATORS = 0x04,
|
||||
KEYCHRON_KC_RGB_LED_COUNT = 0x05,
|
||||
KEYCHRON_KC_RGB_LED_IDX = 0x06,
|
||||
KEYCHRON_KC_RGB_PER_KEY_GET_TYPE = 0x07,
|
||||
KEYCHRON_KC_RGB_PER_KEY_SET_TYPE = 0x08,
|
||||
KEYCHRON_KC_RGB_PER_KEY_GET_COLOR = 0x09,
|
||||
KEYCHRON_KC_RGB_PER_KEY_SET_COLOR = 0x0A,
|
||||
KEYCHRON_KC_RGB_MIXED_GET_INFO = 0x0B,
|
||||
KEYCHRON_KC_RGB_MIXED_GET_REGIONS = 0x0C,
|
||||
KEYCHRON_KC_RGB_MIXED_SET_REGIONS = 0x0D,
|
||||
KEYCHRON_KC_RGB_MIXED_GET_EFFECTS = 0x0E,
|
||||
KEYCHRON_KC_RGB_MIXED_SET_EFFECTS = 0x0F,
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Per-key RGB animation types (PER_KEY_RGB_SET_TYPE value) |
|
||||
\*---------------------------------------------------------*/
|
||||
enum KeychronPerKeyRgbType
|
||||
{
|
||||
KEYCHRON_PER_KEY_RGB_SOLID = 0,
|
||||
KEYCHRON_PER_KEY_RGB_BREATHING = 1,
|
||||
KEYCHRON_PER_KEY_RGB_REACTIVE_SIMPLE = 2,
|
||||
KEYCHRON_PER_KEY_RGB_REACTIVE_WIDE = 3,
|
||||
KEYCHRON_PER_KEY_RGB_REACTIVE_SPLASH = 4,
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| VIA backlight config commands |
|
||||
| |
|
||||
| Standard QMK/VIA rgb_matrix control. |
|
||||
| Format: [cmd, value_type, value_id, value] |
|
||||
| value_type = 3 for rgb_matrix |
|
||||
\*---------------------------------------------------------*/
|
||||
enum KeychronVIABacklightCommand
|
||||
{
|
||||
KEYCHRON_VIA_BACKLIGHT_SET_VALUE = 0x07,
|
||||
KEYCHRON_VIA_BACKLIGHT_GET_VALUE = 0x08,
|
||||
KEYCHRON_VIA_BACKLIGHT_SAVE = 0x09,
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| VIA backlight value IDs |
|
||||
\*---------------------------------------------------------*/
|
||||
#define KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX 0x03
|
||||
|
||||
enum KeychronVIABacklightValueID
|
||||
{
|
||||
KEYCHRON_VIA_BACKLIGHT_BRIGHTNESS = 0x01,
|
||||
KEYCHRON_VIA_BACKLIGHT_EFFECT = 0x02,
|
||||
KEYCHRON_VIA_BACKLIGHT_SPEED = 0x03,
|
||||
KEYCHRON_VIA_BACKLIGHT_COLOR = 0x04,
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Q1 HE effect IDs |
|
||||
| |
|
||||
| Determined by the number of standard effects enabled |
|
||||
| in the Q1 HE firmware (info.json animations) + |
|
||||
| 2 custom effects (PER_KEY_RGB, MIXED_RGB). |
|
||||
| May need adjustment for other firmware versions. |
|
||||
\*---------------------------------------------------------*/
|
||||
#define KEYCHRON_QHE_PER_KEY_RGB_EFFECT 23
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Brightness and speed ranges |
|
||||
\*---------------------------------------------------------*/
|
||||
#define KEYCHRON_QHE_MIN_BRIGHTNESS 0x00
|
||||
#define KEYCHRON_QHE_MAX_BRIGHTNESS 0xFF
|
||||
#define KEYCHRON_QHE_MIN_SPEED 0x00
|
||||
#define KEYCHRON_QHE_MAX_SPEED 0xFF
|
||||
|
||||
class QMKKeychronController
|
||||
{
|
||||
public:
|
||||
QMKKeychronController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name);
|
||||
~QMKKeychronController();
|
||||
|
||||
std::string GetDeviceLocation();
|
||||
std::string GetNameString();
|
||||
std::string GetSerialString();
|
||||
|
||||
unsigned int GetLedCount();
|
||||
std::vector<int> GetLedNumbersByRow(unsigned char row);
|
||||
std::vector<std::vector<int>> GetAllLedNumbers(unsigned char num_rows);
|
||||
|
||||
void SetPerKeyRgbColor(unsigned char start, unsigned char count, const std::vector<unsigned char>& hsv_data);
|
||||
std::vector<unsigned char> GetPerKeyRgbColor(unsigned char start, unsigned char count);
|
||||
|
||||
void SetRgbMatrixMode(unsigned char mode);
|
||||
unsigned char GetRgbMatrixMode();
|
||||
|
||||
void SetPerKeyRgbType(unsigned char type);
|
||||
|
||||
void SetBrightness(unsigned char brightness);
|
||||
unsigned char GetBrightness();
|
||||
|
||||
void SetSpeed(unsigned char speed);
|
||||
unsigned char GetSpeed();
|
||||
|
||||
void SetColorHSV(unsigned char h, unsigned char s);
|
||||
void SaveLedConf();
|
||||
|
||||
private:
|
||||
hid_device* dev;
|
||||
std::string location;
|
||||
std::string name;
|
||||
|
||||
void SendPacket(unsigned char* data, size_t len);
|
||||
int ReadPacket(unsigned char* buf, size_t buf_len);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| QMKKeychronControllerDetect.cpp |
|
||||
| |
|
||||
| Detector for Keychron QMK-based keyboards |
|
||||
| (Q1 HE and other KEYCHRON_RGB-enabled models) |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "Detector.h"
|
||||
#include "QMKKeychronController.h"
|
||||
#include "RGBController_QMKKeychron.h"
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Q1 HE detector |
|
||||
\*---------------------------------------------------------*/
|
||||
void DetectQMKKeychronController(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(dev)
|
||||
{
|
||||
QMKKeychronController* controller = new QMKKeychronController(dev, *info, name);
|
||||
RGBController_QMKKeychron* rgb_controller = new RGBController_QMKKeychron(controller);
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Register HID detectors |
|
||||
\*---------------------------------------------------------*/
|
||||
REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronController, KEYCHRON_VID, KEYCHRON_Q1_HE_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
|
||||
@@ -0,0 +1,303 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_QMKKeychron.cpp |
|
||||
| |
|
||||
| RGBController for Keychron QMK-based keyboards |
|
||||
| (Q1 HE and other KEYCHRON_RGB-enabled models) |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <cmath>
|
||||
#include "RGBControllerKeyNames.h"
|
||||
#include "RGBController_QMKKeychron.h"
|
||||
#include "QMKKeychronController.h"
|
||||
|
||||
#define NA 0xFFFFFFFF
|
||||
|
||||
/**------------------------------------------------------------------*\
|
||||
@name Keychron Q1 HE
|
||||
@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;
|
||||
} keychron_qhe_effect;
|
||||
|
||||
static const keychron_qhe_effect qhe_effects[] =
|
||||
{
|
||||
{ "Direct", -1, MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS },
|
||||
{ "Solid Color", 0, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Breathing", 1, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Band Spiral", 2, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Cycle All", 3, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Cycle Left Right", 4, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Cycle Up Down", 5, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Cycle Out In", 6, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Cycle Out In Dual", 7, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Cycle Pinwheel", 8, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Cycle Spiral", 9, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Dual Beacon", 10, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Rainbow Beacon", 11, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Rainbow Moving Chevron", 12, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Jellybean Raindrops", 13, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Pixel Rain", 14, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Typing Heatmap", 15, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Digital Rain", 16, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Solid Reactive Simple", 17, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Solid Reactive Multiwide", 18, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Solid Reactive Multinexus", 19, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Splash", 20, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
{ "Solid Splash", 21, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE },
|
||||
};
|
||||
|
||||
RGBController_QMKKeychron::RGBController_QMKKeychron(QMKKeychronController* controller_ptr)
|
||||
{
|
||||
controller = controller_ptr;
|
||||
|
||||
name = controller->GetNameString();
|
||||
vendor = "Keychron";
|
||||
type = DEVICE_TYPE_KEYBOARD;
|
||||
description = name;
|
||||
location = controller->GetDeviceLocation();
|
||||
serial = controller->GetSerialString();
|
||||
|
||||
for(const keychron_qhe_effect& effect : qhe_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()
|
||||
{
|
||||
unsigned int led_count = controller->GetLedCount();
|
||||
|
||||
zone keyboard_zone;
|
||||
keyboard_zone.name = ZONE_EN_KEYBOARD;
|
||||
keyboard_zone.type = ZONE_TYPE_MATRIX;
|
||||
|
||||
if(led_count > 0)
|
||||
{
|
||||
keyboard_zone.matrix_map = new matrix_map_type;
|
||||
keyboard_zone.matrix_map->height = 6;
|
||||
keyboard_zone.matrix_map->width = 16;
|
||||
|
||||
keyboard_zone.matrix_map->map = new unsigned int[6 * 16];
|
||||
memset(keyboard_zone.matrix_map->map, 0xFF, 6 * 16 * sizeof(unsigned int));
|
||||
|
||||
std::vector<std::vector<int>> led_map = controller->GetAllLedNumbers(6);
|
||||
|
||||
unsigned int led_idx = 0;
|
||||
for(unsigned int h = 0; h < 6 && h < led_map.size(); h++)
|
||||
{
|
||||
for(unsigned int w = 0; w < 16 && w < led_map[h].size(); w++)
|
||||
{
|
||||
int val = led_map[h][w];
|
||||
keyboard_zone.matrix_map->map[h * 16 + w] = (val >= 0) ? (unsigned int)val : NA;
|
||||
|
||||
if(val >= 0)
|
||||
{
|
||||
led new_led;
|
||||
new_led.name = "Key: " + std::to_string(val);
|
||||
leds.push_back(new_led);
|
||||
led_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyboard_zone.leds_min = led_idx;
|
||||
keyboard_zone.leds_max = led_idx;
|
||||
keyboard_zone.leds_count = led_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyboard_zone.matrix_map = nullptr;
|
||||
keyboard_zone.leds_min = 0;
|
||||
keyboard_zone.leds_max = 0;
|
||||
keyboard_zone.leds_count = 0;
|
||||
}
|
||||
|
||||
zones.push_back(keyboard_zone);
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
void RGBController_QMKKeychron::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||
{
|
||||
}
|
||||
|
||||
void RGBController_QMKKeychron::DeviceUpdateLEDs()
|
||||
{
|
||||
UpdateZoneLEDs(0);
|
||||
}
|
||||
|
||||
void RGBController_QMKKeychron::UpdateZoneLEDs(int /*zone*/)
|
||||
{
|
||||
if(active_mode == 0)
|
||||
{
|
||||
controller->SetRgbMatrixMode(KEYCHRON_QHE_PER_KEY_RGB_EFFECT);
|
||||
controller->SetPerKeyRgbType(KEYCHRON_PER_KEY_RGB_SOLID);
|
||||
|
||||
unsigned int total_leds = leds.size();
|
||||
unsigned char max_per_packet = 9;
|
||||
|
||||
for(unsigned char start = 0; start < total_leds; start += max_per_packet)
|
||||
{
|
||||
unsigned char count = (unsigned char)((total_leds - start) > max_per_packet ? max_per_packet : (total_leds - start));
|
||||
|
||||
std::vector<unsigned char> hsv_data;
|
||||
hsv_data.reserve(count * 3);
|
||||
|
||||
for(unsigned char i = 0; i < count; i++)
|
||||
{
|
||||
RGBColor color = colors[start + i];
|
||||
unsigned char r = RGBGetRValue(color);
|
||||
unsigned char g = RGBGetGValue(color);
|
||||
unsigned char b = RGBGetBValue(color);
|
||||
unsigned char h, s, v;
|
||||
|
||||
RGBToHSV(r, g, b, h, s, v);
|
||||
|
||||
hsv_data.push_back(h);
|
||||
hsv_data.push_back(s);
|
||||
hsv_data.push_back(v);
|
||||
}
|
||||
|
||||
controller->SetPerKeyRgbColor(start, count, hsv_data);
|
||||
}
|
||||
|
||||
controller->SetBrightness(modes[active_mode].brightness);
|
||||
}
|
||||
else
|
||||
{
|
||||
int effect_id = modes[active_mode].value;
|
||||
|
||||
controller->SetRgbMatrixMode((unsigned char)effect_id);
|
||||
controller->SetBrightness(modes[active_mode].brightness);
|
||||
controller->SetSpeed(modes[active_mode].speed);
|
||||
|
||||
if(modes[active_mode].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR)
|
||||
{
|
||||
RGBColor color = modes[active_mode].colors[0];
|
||||
unsigned char r = RGBGetRValue(color);
|
||||
unsigned char g = RGBGetGValue(color);
|
||||
unsigned char b = RGBGetBValue(color);
|
||||
unsigned char h, s, v;
|
||||
|
||||
RGBToHSV(r, g, b, h, s, v);
|
||||
|
||||
controller->SetColorHSV(h, s);
|
||||
}
|
||||
|
||||
controller->SaveLedConf();
|
||||
}
|
||||
}
|
||||
|
||||
void RGBController_QMKKeychron::UpdateSingleLED(int /*led*/)
|
||||
{
|
||||
UpdateZoneLEDs(0);
|
||||
}
|
||||
|
||||
void RGBController_QMKKeychron::DeviceUpdateMode()
|
||||
{
|
||||
UpdateZoneLEDs(0);
|
||||
}
|
||||
|
||||
void RGBController_QMKKeychron::RGBToHSV(unsigned char r, unsigned char g, unsigned char b, unsigned char& h, unsigned char& s, unsigned char& v)
|
||||
{
|
||||
double rd = r / 255.0;
|
||||
double gd = g / 255.0;
|
||||
double bd = b / 255.0;
|
||||
|
||||
double max_val = rd > gd ? (rd > bd ? rd : bd) : (gd > bd ? gd : bd);
|
||||
double min_val = rd < gd ? (rd < bd ? rd : bd) : (gd < bd ? gd : bd);
|
||||
double delta = max_val - min_val;
|
||||
|
||||
v = (unsigned char)(max_val * 255.0);
|
||||
|
||||
if(delta < 0.00001)
|
||||
{
|
||||
h = 0;
|
||||
s = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
s = (unsigned char)((max_val > 0.0) ? (delta / max_val) * 255.0 : 0.0);
|
||||
|
||||
double hd;
|
||||
if(max_val == rd)
|
||||
{
|
||||
hd = (gd - bd) / delta;
|
||||
if(gd < bd) hd += 6.0;
|
||||
}
|
||||
else if(max_val == gd)
|
||||
{
|
||||
hd = (bd - rd) / delta + 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
hd = (rd - gd) / delta + 4.0;
|
||||
}
|
||||
|
||||
h = (unsigned char)((hd / 6.0) * 255.0);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_QMKKeychron.h |
|
||||
| |
|
||||
| RGBController for Keychron QMK-based keyboards |
|
||||
| (Q1 HE and other KEYCHRON_RGB-enabled models) |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RGBController.h"
|
||||
#include "QMKKeychronController.h"
|
||||
|
||||
class RGBController_QMKKeychron : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_QMKKeychron(QMKKeychronController* controller_ptr);
|
||||
~RGBController_QMKKeychron();
|
||||
|
||||
void SetupZones();
|
||||
void ResizeZone(int zone, int new_size);
|
||||
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
|
||||
void DeviceUpdateMode();
|
||||
|
||||
private:
|
||||
QMKKeychronController* controller;
|
||||
|
||||
static void RGBToHSV(unsigned char r, unsigned char g, unsigned char b, unsigned char& h, unsigned char& s, unsigned char& v);
|
||||
};
|
||||
Reference in New Issue
Block a user