Publish curated LumaOps source from c85ee9ef8f1454f5c305398902afbb9e6738de40
Managed validation / full (push) Failing after 8s

This commit is contained in:
Public Source Publisher committed 2026-09-30 00:26:45 +00:00
1 parent 91f8b3e3c1
commit def7920856
2271 files changed
+501225 -501178

No files matched your search

+21 -21
View File
@@ -1,22 +1,22 @@
/*---------------------------------------------------------*\
| QMKCommon.h |
| |
| Common QMK definitions |
| |
| Adam Honse <calcprogrammer1@gmail.com) 24 Jun 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
typedef struct
{
unsigned char x; /* X position in RGB matrix (0-224) */
unsigned char y; /* Y position in RGB matrix (0-64) */
unsigned char flags; /* LED flags */
unsigned char row; /* Row in key matrix */
unsigned char col; /* Column in key matrix */
bool valid; /* Is this LED valid? */
/*---------------------------------------------------------*\
| QMKCommon.h |
| |
| Common QMK definitions |
| |
| Adam Honse <calcprogrammer1@gmail.com) 24 Jun 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
typedef struct
{
unsigned char x; /* X position in RGB matrix (0-224) */
unsigned char y; /* Y position in RGB matrix (0-64) */
unsigned char flags; /* LED flags */
unsigned char row; /* Row in key matrix */
unsigned char col; /* Column in key matrix */
bool valid; /* Is this LED valid? */
} qmk_rgb_matrix_led_info;
File diff suppressed because it is too large. Load diff
@@ -1,448 +1,448 @@
/*---------------------------------------------------------*\
| QMKKeychronController.h |
| |
| Driver 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 |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "QMKCommon.h"
#include "RGBController.h"
/*---------------------------------------------------------*\
| Keychron vendor ID |
\*---------------------------------------------------------*/
#define KEYCHRON_VID 0x3434
/*---------------------------------------------------------*\
| Product IDs |
\*---------------------------------------------------------*/
#define KEYCHRON_C1_PRO_ANSI_RGB_PID 0x0510
#define KEYCHRON_C1_PRO_8K_ANSI_PID 0x0521
#define KEYCHRON_C1_PRO_8K_ISO_PID 0x051D
#define KEYCHRON_C1_PRO_8K_JIS_PID 0x051E
#define KEYCHRON_C1_PRO_V2_ANSI_RGB_PID 0x0516
#define KEYCHRON_C2_PRO_ANSI_RGB_PID 0x0520
#define KEYCHRON_C2_PRO_8K_ANSI_PID 0x0522
#define KEYCHRON_C2_PRO_8K_ISO_PID 0x052D
#define KEYCHRON_C2_PRO_V2_ANSI_RGB_PID 0x0526
#define KEYCHRON_C3_PRO_ANSI_RGB_PID 0x0433
#define KEYCHRON_C3_PRO_8K_ANSI_PID 0x0530
#define KEYCHRON_C3_PRO_8K_ISO_PID 0x0531
#define KEYCHRON_C3_PRO_8K_JIS_PID 0x0532
#define KEYCHRON_K0_MAX_PID 0x0A06
#define KEYCHRON_K1_MAX_ANSI_RGB_PID 0x0A10
#define KEYCHRON_K1_MAX_ISO_RGB_PID 0x0A11
#define KEYCHRON_K1_MAX_JIS_RGB_PID 0x0A12
#define KEYCHRON_K1_V6_ANSI_RGB_PID 0x0D10
#define KEYCHRON_K1_V6_ISO_RGB_PID 0x0D11
#define KEYCHRON_K1_V6_JIS_RGB_PID 0x0D12
#define KEYCHRON_K2_HE_ANSI_PID 0x0E20
#define KEYCHRON_K2_HE_ISO_PID 0x0E21
#define KEYCHRON_K2_HE_JIS_PID 0x0E22
#define KEYCHRON_K2_MAX_ANSI_RGB_PID 0x0A20
#define KEYCHRON_K2_MAX_ISO_RGB_PID 0x0A21
#define KEYCHRON_K2_MAX_JIS_RGB_PID 0x0A22
#define KEYCHRON_K2_V3_ANSI_RGB_PID 0x0D20
#define KEYCHRON_K2_V3_ISO_RGB_PID 0x0D21
#define KEYCHRON_K2_V3_JIS_RGB_PID 0x0D22
#define KEYCHRON_K3_MAX_ANSI_RGB_PID 0x0A30
#define KEYCHRON_K3_MAX_ISO_RGB_PID 0x0A31
#define KEYCHRON_K3_MAX_JIS_RGB_PID 0x0A32
#define KEYCHRON_K3_V3_ANSI_RGB_PID 0x0D30
#define KEYCHRON_K3_V3_ISO_RGB_PID 0x0D31
#define KEYCHRON_K3_V3_JIS_RGB_PID 0x0D32
#define KEYCHRON_K4_HE_ANSI_PID 0x0E40
#define KEYCHRON_K4_HE_ISO_PID 0x0E41
#define KEYCHRON_K4_HE_JIS_PID 0x0E42
#define KEYCHRON_K4_MAX_ANSI_RGB_PID 0x0A40
#define KEYCHRON_K4_MAX_ISO_RGB_PID 0x0A41
#define KEYCHRON_K4_MAX_JIS_RGB_PID 0x0A42
#define KEYCHRON_K4_V3_ANSI_RGB_PID 0x0D40
#define KEYCHRON_K4_V3_ISO_RGB_PID 0x0D41
#define KEYCHRON_K4_V3_JIS_RGB_PID 0x0D42
#define KEYCHRON_K5_MAX_ANSI_RGB_PID 0x0A50
#define KEYCHRON_K5_MAX_ISO_RGB_PID 0x0A51
#define KEYCHRON_K5_MAX_JIS_RGB_PID 0x0A52
#define KEYCHRON_K5_MAX_JIS_V2_RGB_PID 0x0A58
#define KEYCHRON_K5_V2_ANSI_RGB_PID 0x0D50
#define KEYCHRON_K5_V2_ISO_RGB_PID 0x0D51
#define KEYCHRON_K5_V2_JIS_RGB_PID 0x0D52
#define KEYCHRON_K6_HE_ANSI_PID 0x0E60
#define KEYCHRON_K7_MAX_ANSI_RGB_PID 0x0A70
#define KEYCHRON_K7_MAX_ISO_RGB_PID 0x0A71
#define KEYCHRON_K7_MAX_JIS_RGB_PID 0x0A72
#define KEYCHRON_K7_MAX_JIS_V2_RGB_PID 0x0A76
#define KEYCHRON_K8_HE_ANSI_PID 0x0E80
#define KEYCHRON_K8_HE_ISO_PID 0x0E81
#define KEYCHRON_K8_HE_JIS_PID 0x0E82
#define KEYCHRON_K8_MAX_ANSI_RGB_PID 0x0A80
#define KEYCHRON_K8_MAX_ISO_RGB_PID 0x0A81
#define KEYCHRON_K8_MAX_JIS_RGB_PID 0x0A82
#define KEYCHRON_K8_PRO_ANSI_RGB_PID 0x0280
#define KEYCHRON_K8_PRO_ISO_RGB_PID 0x0281
#define KEYCHRON_K8_PRO_JIS_RGB_PID 0x0282
#define KEYCHRON_K8_V2_ANSI_RGB_PID 0x0D80
#define KEYCHRON_K8_V2_ISO_RGB_PID 0x0D81
#define KEYCHRON_K8_V2_JIS_RGB_PID 0x0D82
#define KEYCHRON_K9_MAX_ANSI_RGB_PID 0x0A90
#define KEYCHRON_K10_HE_ANSI_PID 0x0EA0
#define KEYCHRON_K10_HE_ISO_PID 0x0EA1
#define KEYCHRON_K10_MAX_ANSI_RGB_PID 0x0AA0
#define KEYCHRON_K10_MAX_ISO_RGB_PID 0x0AA1
#define KEYCHRON_K10_MAX_JIS_RGB_PID 0x0AA2
#define KEYCHRON_K10_V2_ANSI_RGB_PID 0x0DA0
#define KEYCHRON_K10_V2_ISO_RGB_PID 0x0DA1
#define KEYCHRON_K10_V2_JIS_RGB_PID 0x0DA2
#define KEYCHRON_K11_MAX_ANSI_ENCODER_RGB_PID 0x0AB3
#define KEYCHRON_K11_MAX_ISO_ENCODER_RGB_PID 0x0AB4
#define KEYCHRON_K11_MAX_JIS_ENCODER_RGB_PID 0x0AB5
#define KEYCHRON_K13_MAX_ANSI_RGB_PID 0x0AD0
#define KEYCHRON_K13_MAX_ISO_RGB_PID 0x0AD1
#define KEYCHRON_K13_MAX_JIS_RGB_PID 0x0AD2
#define KEYCHRON_K15_MAX_ANSI_ENCODER_RGB_PID 0x0AF0
#define KEYCHRON_K15_MAX_ISO_ENCODER_RGB_PID 0x0AF1
#define KEYCHRON_K17_MAX_ANSI_ENCODER_RGB_PID 0x0A00
#define KEYCHRON_K17_MAX_ISO_ENCODER_RGB_PID 0x0A01
#define KEYCHRON_K17_MAX_JIS_ENCODER_RGB_PID 0x0A02
#define KEYCHRON_Q0_BASE_PID 0x0130
#define KEYCHRON_Q0_PLUS_PID 0x0131
#define KEYCHRON_Q0_MAX_ENCODER_PID 0x0800
#define KEYCHRON_Q1_HE_ANSI_ENCODER_PID 0x0B10
#define KEYCHRON_Q1_HE_ISO_ENCODER_PID 0x0B11
#define KEYCHRON_Q1_HE_JIS_ENCODER_PID 0x0B12
#define KEYCHRON_Q1_MAX_ANSI_ENCODER_PID 0x0810
#define KEYCHRON_Q1_MAX_ISO_ENCODER_PID 0x0811
#define KEYCHRON_Q1_MAX_JIS_ENCODER_PID 0x0812
#define KEYCHRON_Q1_V1_ANSI_PID 0x0100
#define KEYCHRON_Q1_V1_ANSI_ENCODER_PID 0x0101
#define KEYCHRON_Q1_V1_ISO_PID 0x0102
#define KEYCHRON_Q1_V1_ISO_ENCODER_PID 0x0103
#define KEYCHRON_Q1_V2_ANSI_PID 0x0106
#define KEYCHRON_Q1_V2_ANSI_ENCODER_PID 0x0107
#define KEYCHRON_Q1_V2_ISO_PID 0x0108
#define KEYCHRON_Q1_V2_ISO_ENCODER_PID 0x0109
#define KEYCHRON_Q1_V2_JIS_PID 0x010A
#define KEYCHRON_Q1_V2_JIS_ENCODER_PID 0x010B
#define KEYCHRON_Q2_ANSI_PID 0x0110
#define KEYCHRON_Q2_ANSI_ENCODER_PID 0x0111
#define KEYCHRON_Q2_ISO_PID 0x0112
#define KEYCHRON_Q2_ISO_ENCODER_PID 0x0113
#define KEYCHRON_Q2_JIS_PID 0x0114
#define KEYCHRON_Q2_JIS_ENCODER_PID 0x0115
#define KEYCHRON_Q2_HE_ANSI_ENCODER_PID 0x0B20
#define KEYCHRON_Q2_MAX_ANSI_ENCODER_PID 0x0820
#define KEYCHRON_Q2_MAX_ISO_ENCODER_PID 0x0821
#define KEYCHRON_Q3_ANSI_PID 0x0120
#define KEYCHRON_Q3_ANSI_ENCODER_PID 0x0121
#define KEYCHRON_Q3_ISO_PID 0x0122
#define KEYCHRON_Q3_ISO_ENCODER_PID 0x0123
#define KEYCHRON_Q3_JIS_PID 0x0124
#define KEYCHRON_Q3_JIS_ENCODER_PID 0x0125
#define KEYCHRON_Q3_HE_ANSI_ENCODER_PID 0x0B30
#define KEYCHRON_Q3_HE_ISO_ENCODER_PID 0x0B31
#define KEYCHRON_Q3_HE_JIS_ENCODER_PID 0x0B32
#define KEYCHRON_Q3_MAX_ANSI_ENCODER_PID 0x0830
#define KEYCHRON_Q3_MAX_ISO_ENCODER_PID 0x0831
#define KEYCHRON_Q4_ANSI_PID 0x0140
#define KEYCHRON_Q4_ISO_PID 0x0142
#define KEYCHRON_Q4_HE_ANSI_PID 0x0B40
#define KEYCHRON_Q5_ANSI_PID 0x0150
#define KEYCHRON_Q5_ANSI_ENCODER_PID 0x0151
#define KEYCHRON_Q5_ISO_PID 0x0152
#define KEYCHRON_Q5_ISO_ENCODER_PID 0x0153
#define KEYCHRON_Q5_HE_ANSI_ENCODER_PID 0x0B50
#define KEYCHRON_Q5_HE_ISO_ENCODER_PID 0x0B51
#define KEYCHRON_Q5_HE_JIS_ENCODER_PID 0x0B52
#define KEYCHRON_Q5_MAX_ANSI_ENCODER_PID 0x0850
#define KEYCHRON_Q5_MAX_ISO_ENCODER_PID 0x0851
#define KEYCHRON_Q5_MAX_JIS_ENCODER_PID 0x0852
#define KEYCHRON_Q6_ANSI_PID 0x0160
#define KEYCHRON_Q6_ANSI_ENCODER_PID 0x0161
#define KEYCHRON_Q6_ISO_PID 0x0162
#define KEYCHRON_Q6_ISO_ENCODER_PID 0x0163
#define KEYCHRON_Q6_HE_ANSI_ENCODER_PID 0x0B60
#define KEYCHRON_Q6_HE_ISO_ENCODER_PID 0x0B61
#define KEYCHRON_Q6_HE_JIS_ENCODER_PID 0x0B62
#define KEYCHRON_Q6_MAX_ANSI_ENCODER_PID 0x0860
#define KEYCHRON_Q6_MAX_ISO_ENCODER_PID 0x0861
#define KEYCHRON_Q7_ANSI_PID 0x0170
#define KEYCHRON_Q7_ISO_PID 0x0172
#define KEYCHRON_Q8_ANSI_PID 0x0180
#define KEYCHRON_Q8_ANSI_ENCODER_PID 0x0181
#define KEYCHRON_Q8_ISO_PID 0x0182
#define KEYCHRON_Q8_ISO_ENCODER_PID 0x0183
#define KEYCHRON_Q8_MAX_ANSI_ENCODER_PID 0x0880
#define KEYCHRON_Q9_ANSI_PID 0x0190
#define KEYCHRON_Q9_ANSI_ENCODER_PID 0x0191
#define KEYCHRON_Q9_ISO_PID 0x0192
#define KEYCHRON_Q9_ISO_ENCODER_PID 0x0193
#define KEYCHRON_Q9_PLUS_ANSI_ENCODER_PID 0x0194
#define KEYCHRON_Q10_ANSI_ENCODER_PID 0x01A1
#define KEYCHRON_Q10_ISO_ENCODER_PID 0x01A3
#define KEYCHRON_Q10_MAX_ANSI_ENCODER_PID 0x08A0
#define KEYCHRON_Q10_MAX_ISO_ENCODER_PID 0x08A1
#define KEYCHRON_Q11_ANSI_ENCODER_PID 0x01E0
#define KEYCHRON_Q11_ISO_ENCODER_PID 0x01E1
#define KEYCHRON_Q12_ANSI_ENCODER_PID 0x01D1
#define KEYCHRON_Q12_ISO_ENCODER_PID 0x01D3
#define KEYCHRON_Q12_HE_ANSI_ENCODER_PID 0x0BC0
#define KEYCHRON_Q12_HE_ISO_ENCODER_PID 0x0BC1
#define KEYCHRON_Q12_MAX_ANSI_ENCODER_PID 0x08C3
#define KEYCHRON_Q12_MAX_ISO_ENCODER_PID 0x08C4
#define KEYCHRON_Q13_MAX_ANSI_ENCODER_PID 0x08D0
#define KEYCHRON_Q13_MAX_JIS_ENCODER_PID 0x08D2
#define KEYCHRON_Q14_MAX_ANSI_ENCODER_PID 0x08E0
#define KEYCHRON_Q15_MAX_ANSI_ENCODER_PID 0x08F0
#define KEYCHRON_Q60_MAX_ANSI_PID 0x08C0
#define KEYCHRON_Q65_MAX_ANSI_ENCODER_PID 0x08B0
#define KEYCHRON_S1_ANSI_RGB_PID 0x0410
#define KEYCHRON_V1_ABNT2_ENCODER_PID 0x0317
#define KEYCHRON_V1_ANSI_PID 0x0310
#define KEYCHRON_V1_ANSI_ENCODER_PID 0x0311
#define KEYCHRON_V1_ISO_PID 0x0312
#define KEYCHRON_V1_ISO_ENCODER_PID 0x0313
#define KEYCHRON_V1_JIS_PID 0x0314
#define KEYCHRON_V1_JIS_ENCODER_PID 0x0315
#define KEYCHRON_V1_8K_ANSI_ENCODER_PID 0x0F10
#define KEYCHRON_V1_MAX_ANSI_ENCODER_PID 0x0913
#define KEYCHRON_V1_MAX_ISO_ENCODER_PID 0x0914
#define KEYCHRON_V1_MAX_JIS_ENCODER_PID 0x0915
#define KEYCHRON_V2_ABNT2_ENCODER_PID 0x0327
#define KEYCHRON_V2_ANSI_PID 0x0320
#define KEYCHRON_V2_ANSI_ENCODER_PID 0x0321
#define KEYCHRON_V2_ISO_PID 0x0322
#define KEYCHRON_V2_ISO_ENCODER_PID 0x0323
#define KEYCHRON_V2_JIS_PID 0x0324
#define KEYCHRON_V2_JIS_ENCODER_PID 0x0325
#define KEYCHRON_V2_MAX_ANSI_ENCODER_PID 0x0920
#define KEYCHRON_V2_MAX_ISO_ENCODER_PID 0x0921
#define KEYCHRON_V2_MAX_JIS_ENCODER_PID 0x0922
#define KEYCHRON_V3_ABNT2_ENCODER_PID 0x0337
#define KEYCHRON_V3_ANSI_PID 0x0330
#define KEYCHRON_V3_ANSI_ENCODER_PID 0x0331
#define KEYCHRON_V3_ISO_PID 0x0332
#define KEYCHRON_V3_ISO_ENCODER_PID 0x0333
#define KEYCHRON_V3_JIS_PID 0x0334
#define KEYCHRON_V3_JIS_ENCODER_PID 0x0335
#define KEYCHRON_V3_MAX_ANSI_ENCODER_PID 0x0933
#define KEYCHRON_V3_MAX_ISO_ENCODER_PID 0x0934
#define KEYCHRON_V3_MAX_JIS_ENCODER_PID 0x0935
#define KEYCHRON_V4_ANSI_PID 0x0340
#define KEYCHRON_V4_ISO_PID 0x0342
#define KEYCHRON_V4_MAX_ANSI_PID 0x0940
#define KEYCHRON_V4_MAX_ISO_PID 0x0941
#define KEYCHRON_V5_ANSI_PID 0x0350
#define KEYCHRON_V5_ANSI_ENCODER_PID 0x0351
#define KEYCHRON_V5_ISO_PID 0x0352
#define KEYCHRON_V5_ISO_ENCODER_PID 0x0353
#define KEYCHRON_V5_MAX_ANSI_ENCODER_PID 0x0950
#define KEYCHRON_V5_MAX_ISO_ENCODER_PID 0x0951
#define KEYCHRON_V5_MAX_JIS_ENCODER_PID 0x0952
#define KEYCHRON_V6_ABNT2_ENCODER_PID 0x0367
#define KEYCHRON_V6_ANSI_PID 0x0360
#define KEYCHRON_V6_ANSI_ENCODER_PID 0x0361
#define KEYCHRON_V6_ISO_PID 0x0362
#define KEYCHRON_V6_ISO_ENCODER_PID 0x0363
#define KEYCHRON_V6_MAX_ANSI_ENCODER_PID 0x0960
#define KEYCHRON_V6_MAX_ISO_ENCODER_PID 0x0961
#define KEYCHRON_V6_MAX_JIS_ENCODER_PID 0x0962
#define KEYCHRON_V6_V2_ISO_ENCODER_PID 0x0368
#define KEYCHRON_V7_ANSI_PID 0x0370
#define KEYCHRON_V7_ISO_PID 0x0372
#define KEYCHRON_V8_ANSI_PID 0x0380
#define KEYCHRON_V8_ANSI_ENCODER_PID 0x0381
#define KEYCHRON_V8_ISO_PID 0x0382
#define KEYCHRON_V8_ISO_ENCODER_PID 0x0383
#define KEYCHRON_V8_MAX_ANSI_ENCODER_PID 0x0980
#define KEYCHRON_V8_MAX_ISO_ENCODER_PID 0x0981
#define KEYCHRON_V10_ANSI_ENCODER_PID 0x03A1
#define KEYCHRON_V10_ISO_ENCODER_PID 0x03A3
#define KEYCHRON_V10_MAX_ANSI_ENCODER_PID 0x09A0
#define KEYCHRON_V10_MAX_ISO_ENCODER_PID 0x09A1
/*---------------------------------------------------------*\
| 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
/*---------------------------------------------------------*\
| Keychron-specific VIA protocol extension |
\*---------------------------------------------------------*/
enum
{
KC_GET_PROTOCOL_VERSION = 0xA0,
KC_GET_FIRMWARE_VERSION = 0xA1,
KC_GET_SUPPORT_FEATURE = 0xA2,
KC_GET_DEFAULT_LAYER = 0xA3,
KC_MISC_CMD_GROUP = 0xA7,
KC_KEYCHRON_RGB = 0xA8,
KC_ANALOG_MATRIX = 0xA9,
KC_WIRELESS_DFU = 0xAA,
KC_FACTORY_TEST = 0xAB
};
enum KeychronKCRGBCommand
{
KEYCHRON_RGB_PROTOCOL_VER = 0x01,
KEYCHRON_RGB_SAVE = 0x02,
KEYCHRON_RGB_GET_INDICATORS = 0x03,
KEYCHRON_RGB_SET_INDICATORS = 0x04,
KEYCHRON_RGB_LED_COUNT = 0x05,
KEYCHRON_RGB_LED_IDX = 0x06,
KEYCHRON_RGB_PER_KEY_GET_TYPE = 0x07,
KEYCHRON_RGB_PER_KEY_SET_TYPE = 0x08,
KEYCHRON_RGB_PER_KEY_GET_COLOR = 0x09,
KEYCHRON_RGB_PER_KEY_SET_COLOR = 0x0A,
KEYCHRON_RGB_MIXED_GET_INFO = 0x0B,
KEYCHRON_RGB_MIXED_GET_REGIONS = 0x0C,
KEYCHRON_RGB_MIXED_SET_REGIONS = 0x0D,
KEYCHRON_RGB_MIXED_GET_EFFECTS = 0x0E,
KEYCHRON_RGB_MIXED_SET_EFFECTS = 0x0F,
};
enum
{
KC_FEATURE_DEFAULT_LAYER = ( 1 << 0 ),
KC_FEATURE_BLUETOOTH = ( 1 << 1 ),
KC_FEATURE_P24G = ( 1 << 2 ),
KC_FEATURE_ANALOG_MATRIX = ( 1 << 3 ),
KC_FEATURE_STATE_NOTIFY = ( 1 << 4 ),
KC_FEATURE_DYNAMIC_DEBOUNCE = ( 1 << 5 ),
KC_FEATURE_SNAP_CLICK = ( 1 << 6 ),
KC_FEATURE_KEYCHRON_RGB = ( 1 << 7 ),
KC_FEATURE_QUICK_START = ( 1 << 8 ),
KC_FEATURE_NKRO = ( 1 << 9 ),
};
/*---------------------------------------------------------*\
| 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 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 char *path);
~QMKKeychronController();
std::string GetLocation();
std::string GetName();
std::string GetSerial();
std::string GetVendor();
std::string GetVersion();
bool GetSupported();
unsigned short GetKeycode(unsigned short led_index);
unsigned short GetLEDCount();
qmk_rgb_matrix_led_info GetLEDInfo(unsigned short led_index);
void SendLEDs(unsigned short number_leds, RGBColor* color_data);
void SetMode(unsigned short mode, unsigned char speed, unsigned char hue, unsigned char sat, unsigned char val);
void SaveMode();
private:
hid_device* dev;
std::string kc_firmware_version;
unsigned char kc_protocol_version;
unsigned short kc_rgb_protocol_version;
std::vector<unsigned short> keycodes;
std::vector<qmk_rgb_matrix_led_info> led_info;
std::string location;
std::string name;
unsigned short number_leds;
std::string serial;
unsigned short supported_features;
std::string vendor;
unsigned short via_protocol_version;
unsigned short CmdGetKeycode(unsigned char layer, unsigned char row, unsigned char col);
std::string CmdGetKeychronFirmwareVersion();
void CmdGetKeychronProtocolVersion(unsigned char* kc_protocol_version);
void CmdGetKeychronRGBProtocolVersion(unsigned short* kc_rgb_protocol_version);
std::vector<unsigned char> CmdGetLEDIndexByRow(unsigned char row);
void CmdGetNumberLEDs(unsigned short* number_leds);
void CmdGetSupportFeature(unsigned short* supported_features);
void CmdGetViaProtocolVersion(unsigned short* via_protocol_version);
void CmdSaveMode();
void CmdSendLEDs(unsigned char start_index, unsigned char number_leds, RGBColor* color_data);
void CmdSetBrightness(unsigned char brightness);
void CmdSetColorHS(unsigned char h, unsigned char s);
void CmdSetPerKeyRGBType(unsigned char type);
void CmdSetRGBMatrixMode(unsigned char mode);
void CmdSetSpeed(unsigned char speed);
int ViaSendCommand
(
unsigned char cmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
);
int ViaSendCommandSub
(
unsigned char cmd,
unsigned char subcmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
);
};
/*---------------------------------------------------------*\
| QMKKeychronController.h |
| |
| Driver 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 |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "QMKCommon.h"
#include "RGBController.h"
/*---------------------------------------------------------*\
| Keychron vendor ID |
\*---------------------------------------------------------*/
#define KEYCHRON_VID 0x3434
/*---------------------------------------------------------*\
| Product IDs |
\*---------------------------------------------------------*/
#define KEYCHRON_C1_PRO_ANSI_RGB_PID 0x0510
#define KEYCHRON_C1_PRO_8K_ANSI_PID 0x0521
#define KEYCHRON_C1_PRO_8K_ISO_PID 0x051D
#define KEYCHRON_C1_PRO_8K_JIS_PID 0x051E
#define KEYCHRON_C1_PRO_V2_ANSI_RGB_PID 0x0516
#define KEYCHRON_C2_PRO_ANSI_RGB_PID 0x0520
#define KEYCHRON_C2_PRO_8K_ANSI_PID 0x0522
#define KEYCHRON_C2_PRO_8K_ISO_PID 0x052D
#define KEYCHRON_C2_PRO_V2_ANSI_RGB_PID 0x0526
#define KEYCHRON_C3_PRO_ANSI_RGB_PID 0x0433
#define KEYCHRON_C3_PRO_8K_ANSI_PID 0x0530
#define KEYCHRON_C3_PRO_8K_ISO_PID 0x0531
#define KEYCHRON_C3_PRO_8K_JIS_PID 0x0532
#define KEYCHRON_K0_MAX_PID 0x0A06
#define KEYCHRON_K1_MAX_ANSI_RGB_PID 0x0A10
#define KEYCHRON_K1_MAX_ISO_RGB_PID 0x0A11
#define KEYCHRON_K1_MAX_JIS_RGB_PID 0x0A12
#define KEYCHRON_K1_V6_ANSI_RGB_PID 0x0D10
#define KEYCHRON_K1_V6_ISO_RGB_PID 0x0D11
#define KEYCHRON_K1_V6_JIS_RGB_PID 0x0D12
#define KEYCHRON_K2_HE_ANSI_PID 0x0E20
#define KEYCHRON_K2_HE_ISO_PID 0x0E21
#define KEYCHRON_K2_HE_JIS_PID 0x0E22
#define KEYCHRON_K2_MAX_ANSI_RGB_PID 0x0A20
#define KEYCHRON_K2_MAX_ISO_RGB_PID 0x0A21
#define KEYCHRON_K2_MAX_JIS_RGB_PID 0x0A22
#define KEYCHRON_K2_V3_ANSI_RGB_PID 0x0D20
#define KEYCHRON_K2_V3_ISO_RGB_PID 0x0D21
#define KEYCHRON_K2_V3_JIS_RGB_PID 0x0D22
#define KEYCHRON_K3_MAX_ANSI_RGB_PID 0x0A30
#define KEYCHRON_K3_MAX_ISO_RGB_PID 0x0A31
#define KEYCHRON_K3_MAX_JIS_RGB_PID 0x0A32
#define KEYCHRON_K3_V3_ANSI_RGB_PID 0x0D30
#define KEYCHRON_K3_V3_ISO_RGB_PID 0x0D31
#define KEYCHRON_K3_V3_JIS_RGB_PID 0x0D32
#define KEYCHRON_K4_HE_ANSI_PID 0x0E40
#define KEYCHRON_K4_HE_ISO_PID 0x0E41
#define KEYCHRON_K4_HE_JIS_PID 0x0E42
#define KEYCHRON_K4_MAX_ANSI_RGB_PID 0x0A40
#define KEYCHRON_K4_MAX_ISO_RGB_PID 0x0A41
#define KEYCHRON_K4_MAX_JIS_RGB_PID 0x0A42
#define KEYCHRON_K4_V3_ANSI_RGB_PID 0x0D40
#define KEYCHRON_K4_V3_ISO_RGB_PID 0x0D41
#define KEYCHRON_K4_V3_JIS_RGB_PID 0x0D42
#define KEYCHRON_K5_MAX_ANSI_RGB_PID 0x0A50
#define KEYCHRON_K5_MAX_ISO_RGB_PID 0x0A51
#define KEYCHRON_K5_MAX_JIS_RGB_PID 0x0A52
#define KEYCHRON_K5_MAX_JIS_V2_RGB_PID 0x0A58
#define KEYCHRON_K5_V2_ANSI_RGB_PID 0x0D50
#define KEYCHRON_K5_V2_ISO_RGB_PID 0x0D51
#define KEYCHRON_K5_V2_JIS_RGB_PID 0x0D52
#define KEYCHRON_K6_HE_ANSI_PID 0x0E60
#define KEYCHRON_K7_MAX_ANSI_RGB_PID 0x0A70
#define KEYCHRON_K7_MAX_ISO_RGB_PID 0x0A71
#define KEYCHRON_K7_MAX_JIS_RGB_PID 0x0A72
#define KEYCHRON_K7_MAX_JIS_V2_RGB_PID 0x0A76
#define KEYCHRON_K8_HE_ANSI_PID 0x0E80
#define KEYCHRON_K8_HE_ISO_PID 0x0E81
#define KEYCHRON_K8_HE_JIS_PID 0x0E82
#define KEYCHRON_K8_MAX_ANSI_RGB_PID 0x0A80
#define KEYCHRON_K8_MAX_ISO_RGB_PID 0x0A81
#define KEYCHRON_K8_MAX_JIS_RGB_PID 0x0A82
#define KEYCHRON_K8_PRO_ANSI_RGB_PID 0x0280
#define KEYCHRON_K8_PRO_ISO_RGB_PID 0x0281
#define KEYCHRON_K8_PRO_JIS_RGB_PID 0x0282
#define KEYCHRON_K8_V2_ANSI_RGB_PID 0x0D80
#define KEYCHRON_K8_V2_ISO_RGB_PID 0x0D81
#define KEYCHRON_K8_V2_JIS_RGB_PID 0x0D82
#define KEYCHRON_K9_MAX_ANSI_RGB_PID 0x0A90
#define KEYCHRON_K10_HE_ANSI_PID 0x0EA0
#define KEYCHRON_K10_HE_ISO_PID 0x0EA1
#define KEYCHRON_K10_MAX_ANSI_RGB_PID 0x0AA0
#define KEYCHRON_K10_MAX_ISO_RGB_PID 0x0AA1
#define KEYCHRON_K10_MAX_JIS_RGB_PID 0x0AA2
#define KEYCHRON_K10_V2_ANSI_RGB_PID 0x0DA0
#define KEYCHRON_K10_V2_ISO_RGB_PID 0x0DA1
#define KEYCHRON_K10_V2_JIS_RGB_PID 0x0DA2
#define KEYCHRON_K11_MAX_ANSI_ENCODER_RGB_PID 0x0AB3
#define KEYCHRON_K11_MAX_ISO_ENCODER_RGB_PID 0x0AB4
#define KEYCHRON_K11_MAX_JIS_ENCODER_RGB_PID 0x0AB5
#define KEYCHRON_K13_MAX_ANSI_RGB_PID 0x0AD0
#define KEYCHRON_K13_MAX_ISO_RGB_PID 0x0AD1
#define KEYCHRON_K13_MAX_JIS_RGB_PID 0x0AD2
#define KEYCHRON_K15_MAX_ANSI_ENCODER_RGB_PID 0x0AF0
#define KEYCHRON_K15_MAX_ISO_ENCODER_RGB_PID 0x0AF1
#define KEYCHRON_K17_MAX_ANSI_ENCODER_RGB_PID 0x0A00
#define KEYCHRON_K17_MAX_ISO_ENCODER_RGB_PID 0x0A01
#define KEYCHRON_K17_MAX_JIS_ENCODER_RGB_PID 0x0A02
#define KEYCHRON_Q0_BASE_PID 0x0130
#define KEYCHRON_Q0_PLUS_PID 0x0131
#define KEYCHRON_Q0_MAX_ENCODER_PID 0x0800
#define KEYCHRON_Q1_HE_ANSI_ENCODER_PID 0x0B10
#define KEYCHRON_Q1_HE_ISO_ENCODER_PID 0x0B11
#define KEYCHRON_Q1_HE_JIS_ENCODER_PID 0x0B12
#define KEYCHRON_Q1_MAX_ANSI_ENCODER_PID 0x0810
#define KEYCHRON_Q1_MAX_ISO_ENCODER_PID 0x0811
#define KEYCHRON_Q1_MAX_JIS_ENCODER_PID 0x0812
#define KEYCHRON_Q1_V1_ANSI_PID 0x0100
#define KEYCHRON_Q1_V1_ANSI_ENCODER_PID 0x0101
#define KEYCHRON_Q1_V1_ISO_PID 0x0102
#define KEYCHRON_Q1_V1_ISO_ENCODER_PID 0x0103
#define KEYCHRON_Q1_V2_ANSI_PID 0x0106
#define KEYCHRON_Q1_V2_ANSI_ENCODER_PID 0x0107
#define KEYCHRON_Q1_V2_ISO_PID 0x0108
#define KEYCHRON_Q1_V2_ISO_ENCODER_PID 0x0109
#define KEYCHRON_Q1_V2_JIS_PID 0x010A
#define KEYCHRON_Q1_V2_JIS_ENCODER_PID 0x010B
#define KEYCHRON_Q2_ANSI_PID 0x0110
#define KEYCHRON_Q2_ANSI_ENCODER_PID 0x0111
#define KEYCHRON_Q2_ISO_PID 0x0112
#define KEYCHRON_Q2_ISO_ENCODER_PID 0x0113
#define KEYCHRON_Q2_JIS_PID 0x0114
#define KEYCHRON_Q2_JIS_ENCODER_PID 0x0115
#define KEYCHRON_Q2_HE_ANSI_ENCODER_PID 0x0B20
#define KEYCHRON_Q2_MAX_ANSI_ENCODER_PID 0x0820
#define KEYCHRON_Q2_MAX_ISO_ENCODER_PID 0x0821
#define KEYCHRON_Q3_ANSI_PID 0x0120
#define KEYCHRON_Q3_ANSI_ENCODER_PID 0x0121
#define KEYCHRON_Q3_ISO_PID 0x0122
#define KEYCHRON_Q3_ISO_ENCODER_PID 0x0123
#define KEYCHRON_Q3_JIS_PID 0x0124
#define KEYCHRON_Q3_JIS_ENCODER_PID 0x0125
#define KEYCHRON_Q3_HE_ANSI_ENCODER_PID 0x0B30
#define KEYCHRON_Q3_HE_ISO_ENCODER_PID 0x0B31
#define KEYCHRON_Q3_HE_JIS_ENCODER_PID 0x0B32
#define KEYCHRON_Q3_MAX_ANSI_ENCODER_PID 0x0830
#define KEYCHRON_Q3_MAX_ISO_ENCODER_PID 0x0831
#define KEYCHRON_Q4_ANSI_PID 0x0140
#define KEYCHRON_Q4_ISO_PID 0x0142
#define KEYCHRON_Q4_HE_ANSI_PID 0x0B40
#define KEYCHRON_Q5_ANSI_PID 0x0150
#define KEYCHRON_Q5_ANSI_ENCODER_PID 0x0151
#define KEYCHRON_Q5_ISO_PID 0x0152
#define KEYCHRON_Q5_ISO_ENCODER_PID 0x0153
#define KEYCHRON_Q5_HE_ANSI_ENCODER_PID 0x0B50
#define KEYCHRON_Q5_HE_ISO_ENCODER_PID 0x0B51
#define KEYCHRON_Q5_HE_JIS_ENCODER_PID 0x0B52
#define KEYCHRON_Q5_MAX_ANSI_ENCODER_PID 0x0850
#define KEYCHRON_Q5_MAX_ISO_ENCODER_PID 0x0851
#define KEYCHRON_Q5_MAX_JIS_ENCODER_PID 0x0852
#define KEYCHRON_Q6_ANSI_PID 0x0160
#define KEYCHRON_Q6_ANSI_ENCODER_PID 0x0161
#define KEYCHRON_Q6_ISO_PID 0x0162
#define KEYCHRON_Q6_ISO_ENCODER_PID 0x0163
#define KEYCHRON_Q6_HE_ANSI_ENCODER_PID 0x0B60
#define KEYCHRON_Q6_HE_ISO_ENCODER_PID 0x0B61
#define KEYCHRON_Q6_HE_JIS_ENCODER_PID 0x0B62
#define KEYCHRON_Q6_MAX_ANSI_ENCODER_PID 0x0860
#define KEYCHRON_Q6_MAX_ISO_ENCODER_PID 0x0861
#define KEYCHRON_Q7_ANSI_PID 0x0170
#define KEYCHRON_Q7_ISO_PID 0x0172
#define KEYCHRON_Q8_ANSI_PID 0x0180
#define KEYCHRON_Q8_ANSI_ENCODER_PID 0x0181
#define KEYCHRON_Q8_ISO_PID 0x0182
#define KEYCHRON_Q8_ISO_ENCODER_PID 0x0183
#define KEYCHRON_Q8_MAX_ANSI_ENCODER_PID 0x0880
#define KEYCHRON_Q9_ANSI_PID 0x0190
#define KEYCHRON_Q9_ANSI_ENCODER_PID 0x0191
#define KEYCHRON_Q9_ISO_PID 0x0192
#define KEYCHRON_Q9_ISO_ENCODER_PID 0x0193
#define KEYCHRON_Q9_PLUS_ANSI_ENCODER_PID 0x0194
#define KEYCHRON_Q10_ANSI_ENCODER_PID 0x01A1
#define KEYCHRON_Q10_ISO_ENCODER_PID 0x01A3
#define KEYCHRON_Q10_MAX_ANSI_ENCODER_PID 0x08A0
#define KEYCHRON_Q10_MAX_ISO_ENCODER_PID 0x08A1
#define KEYCHRON_Q11_ANSI_ENCODER_PID 0x01E0
#define KEYCHRON_Q11_ISO_ENCODER_PID 0x01E1
#define KEYCHRON_Q12_ANSI_ENCODER_PID 0x01D1
#define KEYCHRON_Q12_ISO_ENCODER_PID 0x01D3
#define KEYCHRON_Q12_HE_ANSI_ENCODER_PID 0x0BC0
#define KEYCHRON_Q12_HE_ISO_ENCODER_PID 0x0BC1
#define KEYCHRON_Q12_MAX_ANSI_ENCODER_PID 0x08C3
#define KEYCHRON_Q12_MAX_ISO_ENCODER_PID 0x08C4
#define KEYCHRON_Q13_MAX_ANSI_ENCODER_PID 0x08D0
#define KEYCHRON_Q13_MAX_JIS_ENCODER_PID 0x08D2
#define KEYCHRON_Q14_MAX_ANSI_ENCODER_PID 0x08E0
#define KEYCHRON_Q15_MAX_ANSI_ENCODER_PID 0x08F0
#define KEYCHRON_Q60_MAX_ANSI_PID 0x08C0
#define KEYCHRON_Q65_MAX_ANSI_ENCODER_PID 0x08B0
#define KEYCHRON_S1_ANSI_RGB_PID 0x0410
#define KEYCHRON_V1_ABNT2_ENCODER_PID 0x0317
#define KEYCHRON_V1_ANSI_PID 0x0310
#define KEYCHRON_V1_ANSI_ENCODER_PID 0x0311
#define KEYCHRON_V1_ISO_PID 0x0312
#define KEYCHRON_V1_ISO_ENCODER_PID 0x0313
#define KEYCHRON_V1_JIS_PID 0x0314
#define KEYCHRON_V1_JIS_ENCODER_PID 0x0315
#define KEYCHRON_V1_8K_ANSI_ENCODER_PID 0x0F10
#define KEYCHRON_V1_MAX_ANSI_ENCODER_PID 0x0913
#define KEYCHRON_V1_MAX_ISO_ENCODER_PID 0x0914
#define KEYCHRON_V1_MAX_JIS_ENCODER_PID 0x0915
#define KEYCHRON_V2_ABNT2_ENCODER_PID 0x0327
#define KEYCHRON_V2_ANSI_PID 0x0320
#define KEYCHRON_V2_ANSI_ENCODER_PID 0x0321
#define KEYCHRON_V2_ISO_PID 0x0322
#define KEYCHRON_V2_ISO_ENCODER_PID 0x0323
#define KEYCHRON_V2_JIS_PID 0x0324
#define KEYCHRON_V2_JIS_ENCODER_PID 0x0325
#define KEYCHRON_V2_MAX_ANSI_ENCODER_PID 0x0920
#define KEYCHRON_V2_MAX_ISO_ENCODER_PID 0x0921
#define KEYCHRON_V2_MAX_JIS_ENCODER_PID 0x0922
#define KEYCHRON_V3_ABNT2_ENCODER_PID 0x0337
#define KEYCHRON_V3_ANSI_PID 0x0330
#define KEYCHRON_V3_ANSI_ENCODER_PID 0x0331
#define KEYCHRON_V3_ISO_PID 0x0332
#define KEYCHRON_V3_ISO_ENCODER_PID 0x0333
#define KEYCHRON_V3_JIS_PID 0x0334
#define KEYCHRON_V3_JIS_ENCODER_PID 0x0335
#define KEYCHRON_V3_MAX_ANSI_ENCODER_PID 0x0933
#define KEYCHRON_V3_MAX_ISO_ENCODER_PID 0x0934
#define KEYCHRON_V3_MAX_JIS_ENCODER_PID 0x0935
#define KEYCHRON_V4_ANSI_PID 0x0340
#define KEYCHRON_V4_ISO_PID 0x0342
#define KEYCHRON_V4_MAX_ANSI_PID 0x0940
#define KEYCHRON_V4_MAX_ISO_PID 0x0941
#define KEYCHRON_V5_ANSI_PID 0x0350
#define KEYCHRON_V5_ANSI_ENCODER_PID 0x0351
#define KEYCHRON_V5_ISO_PID 0x0352
#define KEYCHRON_V5_ISO_ENCODER_PID 0x0353
#define KEYCHRON_V5_MAX_ANSI_ENCODER_PID 0x0950
#define KEYCHRON_V5_MAX_ISO_ENCODER_PID 0x0951
#define KEYCHRON_V5_MAX_JIS_ENCODER_PID 0x0952
#define KEYCHRON_V6_ABNT2_ENCODER_PID 0x0367
#define KEYCHRON_V6_ANSI_PID 0x0360
#define KEYCHRON_V6_ANSI_ENCODER_PID 0x0361
#define KEYCHRON_V6_ISO_PID 0x0362
#define KEYCHRON_V6_ISO_ENCODER_PID 0x0363
#define KEYCHRON_V6_MAX_ANSI_ENCODER_PID 0x0960
#define KEYCHRON_V6_MAX_ISO_ENCODER_PID 0x0961
#define KEYCHRON_V6_MAX_JIS_ENCODER_PID 0x0962
#define KEYCHRON_V6_V2_ISO_ENCODER_PID 0x0368
#define KEYCHRON_V7_ANSI_PID 0x0370
#define KEYCHRON_V7_ISO_PID 0x0372
#define KEYCHRON_V8_ANSI_PID 0x0380
#define KEYCHRON_V8_ANSI_ENCODER_PID 0x0381
#define KEYCHRON_V8_ISO_PID 0x0382
#define KEYCHRON_V8_ISO_ENCODER_PID 0x0383
#define KEYCHRON_V8_MAX_ANSI_ENCODER_PID 0x0980
#define KEYCHRON_V8_MAX_ISO_ENCODER_PID 0x0981
#define KEYCHRON_V10_ANSI_ENCODER_PID 0x03A1
#define KEYCHRON_V10_ISO_ENCODER_PID 0x03A3
#define KEYCHRON_V10_MAX_ANSI_ENCODER_PID 0x09A0
#define KEYCHRON_V10_MAX_ISO_ENCODER_PID 0x09A1
/*---------------------------------------------------------*\
| 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
/*---------------------------------------------------------*\
| Keychron-specific VIA protocol extension |
\*---------------------------------------------------------*/
enum
{
KC_GET_PROTOCOL_VERSION = 0xA0,
KC_GET_FIRMWARE_VERSION = 0xA1,
KC_GET_SUPPORT_FEATURE = 0xA2,
KC_GET_DEFAULT_LAYER = 0xA3,
KC_MISC_CMD_GROUP = 0xA7,
KC_KEYCHRON_RGB = 0xA8,
KC_ANALOG_MATRIX = 0xA9,
KC_WIRELESS_DFU = 0xAA,
KC_FACTORY_TEST = 0xAB
};
enum KeychronKCRGBCommand
{
KEYCHRON_RGB_PROTOCOL_VER = 0x01,
KEYCHRON_RGB_SAVE = 0x02,
KEYCHRON_RGB_GET_INDICATORS = 0x03,
KEYCHRON_RGB_SET_INDICATORS = 0x04,
KEYCHRON_RGB_LED_COUNT = 0x05,
KEYCHRON_RGB_LED_IDX = 0x06,
KEYCHRON_RGB_PER_KEY_GET_TYPE = 0x07,
KEYCHRON_RGB_PER_KEY_SET_TYPE = 0x08,
KEYCHRON_RGB_PER_KEY_GET_COLOR = 0x09,
KEYCHRON_RGB_PER_KEY_SET_COLOR = 0x0A,
KEYCHRON_RGB_MIXED_GET_INFO = 0x0B,
KEYCHRON_RGB_MIXED_GET_REGIONS = 0x0C,
KEYCHRON_RGB_MIXED_SET_REGIONS = 0x0D,
KEYCHRON_RGB_MIXED_GET_EFFECTS = 0x0E,
KEYCHRON_RGB_MIXED_SET_EFFECTS = 0x0F,
};
enum
{
KC_FEATURE_DEFAULT_LAYER = ( 1 << 0 ),
KC_FEATURE_BLUETOOTH = ( 1 << 1 ),
KC_FEATURE_P24G = ( 1 << 2 ),
KC_FEATURE_ANALOG_MATRIX = ( 1 << 3 ),
KC_FEATURE_STATE_NOTIFY = ( 1 << 4 ),
KC_FEATURE_DYNAMIC_DEBOUNCE = ( 1 << 5 ),
KC_FEATURE_SNAP_CLICK = ( 1 << 6 ),
KC_FEATURE_KEYCHRON_RGB = ( 1 << 7 ),
KC_FEATURE_QUICK_START = ( 1 << 8 ),
KC_FEATURE_NKRO = ( 1 << 9 ),
};
/*---------------------------------------------------------*\
| 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 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 char *path);
~QMKKeychronController();
std::string GetLocation();
std::string GetName();
std::string GetSerial();
std::string GetVendor();
std::string GetVersion();
bool GetSupported();
unsigned short GetKeycode(unsigned short led_index);
unsigned short GetLEDCount();
qmk_rgb_matrix_led_info GetLEDInfo(unsigned short led_index);
void SendLEDs(unsigned short number_leds, RGBColor* color_data);
void SetMode(unsigned short mode, unsigned char speed, unsigned char hue, unsigned char sat, unsigned char val);
void SaveMode();
private:
hid_device* dev;
std::string kc_firmware_version;
unsigned char kc_protocol_version;
unsigned short kc_rgb_protocol_version;
std::vector<unsigned short> keycodes;
std::vector<qmk_rgb_matrix_led_info> led_info;
std::string location;
std::string name;
unsigned short number_leds;
std::string serial;
unsigned short supported_features;
std::string vendor;
unsigned short via_protocol_version;
unsigned short CmdGetKeycode(unsigned char layer, unsigned char row, unsigned char col);
std::string CmdGetKeychronFirmwareVersion();
void CmdGetKeychronProtocolVersion(unsigned char* kc_protocol_version);
void CmdGetKeychronRGBProtocolVersion(unsigned short* kc_rgb_protocol_version);
std::vector<unsigned char> CmdGetLEDIndexByRow(unsigned char row);
void CmdGetNumberLEDs(unsigned short* number_leds);
void CmdGetSupportFeature(unsigned short* supported_features);
void CmdGetViaProtocolVersion(unsigned short* via_protocol_version);
void CmdSaveMode();
void CmdSendLEDs(unsigned char start_index, unsigned char number_leds, RGBColor* color_data);
void CmdSetBrightness(unsigned char brightness);
void CmdSetColorHS(unsigned char h, unsigned char s);
void CmdSetPerKeyRGBType(unsigned char type);
void CmdSetRGBMatrixMode(unsigned char mode);
void CmdSetSpeed(unsigned char speed);
int ViaSendCommand
(
unsigned char cmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
);
int ViaSendCommandSub
(
unsigned char cmd,
unsigned char subcmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
);
};
@@ -1,279 +1,279 @@
/*---------------------------------------------------------*\
| QMKKeychronControllerDetect.cpp |
| |
| Detector 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 "Detector.h"
#include "QMKKeychronController.h"
#include "RGBController_QMKKeychron.h"
void DetectQMKKeychronControllers(hid_device_info *info, const std::string&)
{
hid_device *dev = hid_open_path(info->path);
if(dev)
{
QMKKeychronController* controller = new QMKKeychronController(dev, info->path);
if(controller->GetSupported())
{
RGBController_QMKKeychron* rgb_controller = new RGBController_QMKKeychron(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
else
{
delete controller;
}
}
}
REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_8K_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_8K_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_8K_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_8K_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_8K_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_8K_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_8K_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_8K_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K0 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K0_MAX_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_V6_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_V6_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_V6_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_HE_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_V3_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_V3_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_V3_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_V3_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_V3_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_V3_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_HE_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_V3_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_V3_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_V3_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_JIS_V2_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_V2_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_V2_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K6_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_JIS_V2_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_HE_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_PRO_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_PRO_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_V2_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_V2_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K9 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K9_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_V2_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_V2_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K11 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K11_MAX_ANSI_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K11 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K11_MAX_ISO_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K11 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K11_MAX_JIS_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K13_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K13_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K13_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K15 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K15_MAX_ANSI_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K15 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K15_MAX_ISO_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_ANSI_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_ISO_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_JIS_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q0", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_BASE_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q0 Plus", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_PLUS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q0 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_MAX_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q4_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q4_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q4_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q7_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q7_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q9 Plus", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_PLUS_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q11", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q11_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q11", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q11_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q13_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q13_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q14 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q14_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q15 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q15_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q60 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q60_MAX_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q65 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q65_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron S1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_S1_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_8K_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_MAX_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_MAX_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_V2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V7_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V7_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
/*---------------------------------------------------------*\
| QMKKeychronControllerDetect.cpp |
| |
| Detector 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 "Detector.h"
#include "QMKKeychronController.h"
#include "RGBController_QMKKeychron.h"
void DetectQMKKeychronControllers(hid_device_info *info, const std::string&)
{
hid_device *dev = hid_open_path(info->path);
if(dev)
{
QMKKeychronController* controller = new QMKKeychronController(dev, info->path);
if(controller->GetSupported())
{
RGBController_QMKKeychron* rgb_controller = new RGBController_QMKKeychron(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
else
{
delete controller;
}
}
}
REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_8K_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_8K_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_8K_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_8K_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_8K_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_8K_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_8K_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_8K_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K0 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K0_MAX_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_V6_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_V6_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K1 V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_V6_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_HE_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_V3_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_V3_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K2 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_V3_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_V3_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_V3_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K3 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_V3_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_HE_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_V3_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_V3_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K4 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_V3_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_JIS_V2_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_V2_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K5 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_V2_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K6_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_JIS_V2_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_HE_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_PRO_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_PRO_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_V2_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K8 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_V2_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K9 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K9_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_V2_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K10 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_V2_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K11 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K11_MAX_ANSI_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K11 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K11_MAX_ISO_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K11 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K11_MAX_JIS_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K13_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K13_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K13_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K15 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K15_MAX_ANSI_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K15 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K15_MAX_ISO_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_ANSI_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_ISO_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_JIS_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q0", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_BASE_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q0 Plus", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_PLUS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q0 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_MAX_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q4_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q4_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q4_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q7_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q7_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q9 Plus", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_PLUS_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q11", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q11_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q11", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q11_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q12 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q13_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q13_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q14 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q14_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q15 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q15_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q60 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q60_MAX_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron Q65 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q65_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron S1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_S1_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_8K_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_MAX_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_MAX_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V6 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_V2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V7_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V7_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
REGISTER_HID_DETECTOR_IPU("Keychron V10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE);
@@ -1,239 +1,239 @@
/*---------------------------------------------------------*\
| 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++)
{
qmk_rgb_matrix_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++)
{
qmk_rgb_matrix_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();
}
/*---------------------------------------------------------*\
| 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++)
{
qmk_rgb_matrix_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++)
{
qmk_rgb_matrix_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();
}
@@ -1,36 +1,36 @@
/*---------------------------------------------------------*\
| RGBController_QMKKeychron.h |
| |
| 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 |
\*---------------------------------------------------------*/
#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();
void DeviceSaveMode();
private:
QMKKeychronController* controller;
};
/*---------------------------------------------------------*\
| RGBController_QMKKeychron.h |
| |
| 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 |
\*---------------------------------------------------------*/
#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();
void DeviceSaveMode();
private:
QMKKeychronController* controller;
};
+153 -153
View File
@@ -1,153 +1,153 @@
/*---------------------------------------------------------*\
| QMKKeycodes.cpp |
| |
| List of QMK keycode values |
| |
| Adam Honse <calcprogrammer1@gmail.com) 07 Oct 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "QMKKeycodes.h"
#include "RGBControllerKeyNames.h"
std::map<qmk_keycode, std::string> qmk_keynames =
{
{ QMK_KC_NO, KEY_EN_UNUSED },
{ QMK_KC_TRANSPARENT, KEY_EN_RIGHT_FUNCTION },
{ QMK_KC_A, KEY_EN_A },
{ QMK_KC_B, KEY_EN_B },
{ QMK_KC_C, KEY_EN_C },
{ QMK_KC_D, KEY_EN_D },
{ QMK_KC_E, KEY_EN_E },
{ QMK_KC_F, KEY_EN_F },
{ QMK_KC_G, KEY_EN_G },
{ QMK_KC_H, KEY_EN_H },
{ QMK_KC_I, KEY_EN_I },
{ QMK_KC_J, KEY_EN_J },
{ QMK_KC_K, KEY_EN_K },
{ QMK_KC_L, KEY_EN_L },
{ QMK_KC_M, KEY_EN_M },
{ QMK_KC_N, KEY_EN_N },
{ QMK_KC_O, KEY_EN_O },
{ QMK_KC_P, KEY_EN_P },
{ QMK_KC_Q, KEY_EN_Q },
{ QMK_KC_R, KEY_EN_R },
{ QMK_KC_S, KEY_EN_S },
{ QMK_KC_T, KEY_EN_T },
{ QMK_KC_U, KEY_EN_U },
{ QMK_KC_V, KEY_EN_V },
{ QMK_KC_W, KEY_EN_W },
{ QMK_KC_X, KEY_EN_X },
{ QMK_KC_Y, KEY_EN_Y },
{ QMK_KC_Z, KEY_EN_Z },
{ QMK_KC_1, KEY_EN_1 },
{ QMK_KC_2, KEY_EN_2 },
{ QMK_KC_3, KEY_EN_3 },
{ QMK_KC_4, KEY_EN_4 },
{ QMK_KC_5, KEY_EN_5 },
{ QMK_KC_6, KEY_EN_6 },
{ QMK_KC_7, KEY_EN_7 },
{ QMK_KC_8, KEY_EN_8 },
{ QMK_KC_9, KEY_EN_9 },
{ QMK_KC_0, KEY_EN_0 },
{ QMK_KC_ENTER, KEY_EN_ANSI_ENTER },
{ QMK_KC_ESCAPE, KEY_EN_ESCAPE },
{ QMK_KC_BACKSPACE, KEY_EN_BACKSPACE },
{ QMK_KC_TAB, KEY_EN_TAB },
{ QMK_KC_SPACE, KEY_EN_SPACE },
{ QMK_KC_MINUS, KEY_EN_MINUS },
{ QMK_KC_EQUAL, KEY_EN_EQUALS },
{ QMK_KC_LEFT_BRACKET, KEY_EN_LEFT_BRACKET },
{ QMK_KC_RIGHT_BRACKET, KEY_EN_RIGHT_BRACKET },
{ QMK_KC_BACKSLASH, KEY_EN_ANSI_BACK_SLASH },
{ QMK_KC_NONUS_HASH, KEY_EN_POUND },
{ QMK_KC_SEMICOLON, KEY_EN_SEMICOLON },
{ QMK_KC_QUOTE, KEY_EN_QUOTE },
{ QMK_KC_GRAVE, KEY_EN_BACK_TICK },
{ QMK_KC_COMMA, KEY_EN_COMMA },
{ QMK_KC_DOT, KEY_EN_PERIOD },
{ QMK_KC_SLASH, KEY_EN_FORWARD_SLASH },
{ QMK_KC_CAPS_LOCK, KEY_EN_CAPS_LOCK },
{ QMK_KC_F1, KEY_EN_F1 },
{ QMK_KC_F2, KEY_EN_F2 },
{ QMK_KC_F3, KEY_EN_F3 },
{ QMK_KC_F4, KEY_EN_F4 },
{ QMK_KC_F5, KEY_EN_F5 },
{ QMK_KC_F6, KEY_EN_F6 },
{ QMK_KC_F7, KEY_EN_F7 },
{ QMK_KC_F8, KEY_EN_F8 },
{ QMK_KC_F9, KEY_EN_F9 },
{ QMK_KC_F10, KEY_EN_F10 },
{ QMK_KC_F11, KEY_EN_F11 },
{ QMK_KC_F12, KEY_EN_F12 },
{ QMK_KC_PRINT_SCREEN, KEY_EN_PRINT_SCREEN },
{ QMK_KC_SCROLL_LOCK, KEY_EN_SCROLL_LOCK },
{ QMK_KC_PAUSE, KEY_EN_PAUSE_BREAK },
{ QMK_KC_INSERT, KEY_EN_INSERT },
{ QMK_KC_HOME, KEY_EN_HOME },
{ QMK_KC_PAGE_UP, KEY_EN_PAGE_UP },
{ QMK_KC_DELETE, KEY_EN_DELETE },
{ QMK_KC_END, KEY_EN_END },
{ QMK_KC_PAGE_DOWN, KEY_EN_PAGE_DOWN },
{ QMK_KC_RIGHT, KEY_EN_RIGHT_ARROW },
{ QMK_KC_LEFT, KEY_EN_LEFT_ARROW },
{ QMK_KC_DOWN, KEY_EN_DOWN_ARROW },
{ QMK_KC_UP, KEY_EN_UP_ARROW },
{ QMK_KC_NUM_LOCK, KEY_EN_NUMPAD_LOCK },
{ QMK_KC_KP_SLASH, KEY_EN_NUMPAD_DIVIDE },
{ QMK_KC_KP_ASTERISK, KEY_EN_NUMPAD_TIMES },
{ QMK_KC_KP_MINUS, KEY_EN_NUMPAD_MINUS },
{ QMK_KC_KP_PLUS, KEY_EN_NUMPAD_PLUS },
{ QMK_KC_KP_ENTER, KEY_EN_NUMPAD_ENTER },
{ QMK_KC_KP_1, KEY_EN_NUMPAD_1 },
{ QMK_KC_KP_2, KEY_EN_NUMPAD_2 },
{ QMK_KC_KP_3, KEY_EN_NUMPAD_3 },
{ QMK_KC_KP_4, KEY_EN_NUMPAD_4 },
{ QMK_KC_KP_5, KEY_EN_NUMPAD_5 },
{ QMK_KC_KP_6, KEY_EN_NUMPAD_6 },
{ QMK_KC_KP_7, KEY_EN_NUMPAD_7 },
{ QMK_KC_KP_8, KEY_EN_NUMPAD_8 },
{ QMK_KC_KP_9, KEY_EN_NUMPAD_9 },
{ QMK_KC_KP_0, KEY_EN_NUMPAD_0 },
{ QMK_KC_KP_DOT, KEY_EN_NUMPAD_PERIOD },
{ QMK_KC_NONUS_BACKSLASH, KEY_EN_ISO_BACK_SLASH },
{ QMK_KC_APPLICATION, KEY_EN_MENU },
{ QMK_KC_F13, "Key: F13" },
{ QMK_KC_F14, "Key: F14" },
{ QMK_KC_F15, "Key: F15" },
{ QMK_KC_F16, "Key: F16" },
{ QMK_KC_F17, "Key: F17" },
{ QMK_KC_F18, "Key: F18" },
{ QMK_KC_F19, "Key: F19" },
{ QMK_KC_F20, "Key: F20" },
{ QMK_KC_F21, "Key: F21" },
{ QMK_KC_F22, "Key: F22" },
{ QMK_KC_F23, "Key: F23" },
{ QMK_KC_F24, "Key: F24" },
{ QMK_KC_AUDIO_MUTE, KEY_EN_MEDIA_MUTE },
{ QMK_KC_AUDIO_VOL_UP, KEY_EN_MEDIA_VOLUME_UP },
{ QMK_KC_AUDIO_VOL_DOWN, KEY_EN_MEDIA_VOLUME_DOWN },
{ QMK_KC_MEDIA_NEXT_TRACK, KEY_EN_MEDIA_NEXT },
{ QMK_KC_MEDIA_PREV_TRACK, KEY_EN_MEDIA_PREVIOUS },
{ QMK_KC_MEDIA_STOP, KEY_EN_MEDIA_STOP },
{ QMK_KC_MEDIA_PLAY_PAUSE, KEY_EN_MEDIA_PLAY_PAUSE },
{ QMK_KC_MEDIA_SELECT, "Key: Media Select" },
{ QMK_KC_MEDIA_EJECT, "Key: Media Eject" },
{ QMK_KC_BRIGHTNESS_UP, "Key: Brightness Up" },
{ QMK_KC_BRIGHTNESS_DOWN, "Key: Brightness Down" },
{ 196, "Key: Task Manager" }, /* From OpenRGB QMK */
{ 202, "Key: RGB Brightness Up" }, /* From OpenRGB QMK */
{ 203, "Key: RGB Brightness Down" }, /* From OpenRGB QMK */
{ 216, KEY_EN_LEFT_SHIFT }, /* Space Cadet Left Shift */
{ 217, KEY_EN_RIGHT_SHIFT }, /* Space Cadet Right Shift */
{ QMK_KC_LEFT_CTRL, KEY_EN_LEFT_CONTROL },
{ QMK_KC_LEFT_SHIFT, KEY_EN_LEFT_SHIFT },
{ QMK_KC_LEFT_ALT, KEY_EN_LEFT_ALT },
{ QMK_KC_LEFT_GUI, KEY_EN_LEFT_WINDOWS },
{ QMK_KC_RIGHT_CTRL, KEY_EN_RIGHT_CONTROL },
{ QMK_KC_RIGHT_SHIFT, KEY_EN_RIGHT_SHIFT },
{ QMK_KC_RIGHT_ALT, KEY_EN_RIGHT_ALT },
{ QMK_KC_RIGHT_GUI, KEY_EN_RIGHT_WINDOWS },
};
/*---------------------------------------------------------*\
| QMKKeycodes.cpp |
| |
| List of QMK keycode values |
| |
| Adam Honse <calcprogrammer1@gmail.com) 07 Oct 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "QMKKeycodes.h"
#include "RGBControllerKeyNames.h"
std::map<qmk_keycode, std::string> qmk_keynames =
{
{ QMK_KC_NO, KEY_EN_UNUSED },
{ QMK_KC_TRANSPARENT, KEY_EN_RIGHT_FUNCTION },
{ QMK_KC_A, KEY_EN_A },
{ QMK_KC_B, KEY_EN_B },
{ QMK_KC_C, KEY_EN_C },
{ QMK_KC_D, KEY_EN_D },
{ QMK_KC_E, KEY_EN_E },
{ QMK_KC_F, KEY_EN_F },
{ QMK_KC_G, KEY_EN_G },
{ QMK_KC_H, KEY_EN_H },
{ QMK_KC_I, KEY_EN_I },
{ QMK_KC_J, KEY_EN_J },
{ QMK_KC_K, KEY_EN_K },
{ QMK_KC_L, KEY_EN_L },
{ QMK_KC_M, KEY_EN_M },
{ QMK_KC_N, KEY_EN_N },
{ QMK_KC_O, KEY_EN_O },
{ QMK_KC_P, KEY_EN_P },
{ QMK_KC_Q, KEY_EN_Q },
{ QMK_KC_R, KEY_EN_R },
{ QMK_KC_S, KEY_EN_S },
{ QMK_KC_T, KEY_EN_T },
{ QMK_KC_U, KEY_EN_U },
{ QMK_KC_V, KEY_EN_V },
{ QMK_KC_W, KEY_EN_W },
{ QMK_KC_X, KEY_EN_X },
{ QMK_KC_Y, KEY_EN_Y },
{ QMK_KC_Z, KEY_EN_Z },
{ QMK_KC_1, KEY_EN_1 },
{ QMK_KC_2, KEY_EN_2 },
{ QMK_KC_3, KEY_EN_3 },
{ QMK_KC_4, KEY_EN_4 },
{ QMK_KC_5, KEY_EN_5 },
{ QMK_KC_6, KEY_EN_6 },
{ QMK_KC_7, KEY_EN_7 },
{ QMK_KC_8, KEY_EN_8 },
{ QMK_KC_9, KEY_EN_9 },
{ QMK_KC_0, KEY_EN_0 },
{ QMK_KC_ENTER, KEY_EN_ANSI_ENTER },
{ QMK_KC_ESCAPE, KEY_EN_ESCAPE },
{ QMK_KC_BACKSPACE, KEY_EN_BACKSPACE },
{ QMK_KC_TAB, KEY_EN_TAB },
{ QMK_KC_SPACE, KEY_EN_SPACE },
{ QMK_KC_MINUS, KEY_EN_MINUS },
{ QMK_KC_EQUAL, KEY_EN_EQUALS },
{ QMK_KC_LEFT_BRACKET, KEY_EN_LEFT_BRACKET },
{ QMK_KC_RIGHT_BRACKET, KEY_EN_RIGHT_BRACKET },
{ QMK_KC_BACKSLASH, KEY_EN_ANSI_BACK_SLASH },
{ QMK_KC_NONUS_HASH, KEY_EN_POUND },
{ QMK_KC_SEMICOLON, KEY_EN_SEMICOLON },
{ QMK_KC_QUOTE, KEY_EN_QUOTE },
{ QMK_KC_GRAVE, KEY_EN_BACK_TICK },
{ QMK_KC_COMMA, KEY_EN_COMMA },
{ QMK_KC_DOT, KEY_EN_PERIOD },
{ QMK_KC_SLASH, KEY_EN_FORWARD_SLASH },
{ QMK_KC_CAPS_LOCK, KEY_EN_CAPS_LOCK },
{ QMK_KC_F1, KEY_EN_F1 },
{ QMK_KC_F2, KEY_EN_F2 },
{ QMK_KC_F3, KEY_EN_F3 },
{ QMK_KC_F4, KEY_EN_F4 },
{ QMK_KC_F5, KEY_EN_F5 },
{ QMK_KC_F6, KEY_EN_F6 },
{ QMK_KC_F7, KEY_EN_F7 },
{ QMK_KC_F8, KEY_EN_F8 },
{ QMK_KC_F9, KEY_EN_F9 },
{ QMK_KC_F10, KEY_EN_F10 },
{ QMK_KC_F11, KEY_EN_F11 },
{ QMK_KC_F12, KEY_EN_F12 },
{ QMK_KC_PRINT_SCREEN, KEY_EN_PRINT_SCREEN },
{ QMK_KC_SCROLL_LOCK, KEY_EN_SCROLL_LOCK },
{ QMK_KC_PAUSE, KEY_EN_PAUSE_BREAK },
{ QMK_KC_INSERT, KEY_EN_INSERT },
{ QMK_KC_HOME, KEY_EN_HOME },
{ QMK_KC_PAGE_UP, KEY_EN_PAGE_UP },
{ QMK_KC_DELETE, KEY_EN_DELETE },
{ QMK_KC_END, KEY_EN_END },
{ QMK_KC_PAGE_DOWN, KEY_EN_PAGE_DOWN },
{ QMK_KC_RIGHT, KEY_EN_RIGHT_ARROW },
{ QMK_KC_LEFT, KEY_EN_LEFT_ARROW },
{ QMK_KC_DOWN, KEY_EN_DOWN_ARROW },
{ QMK_KC_UP, KEY_EN_UP_ARROW },
{ QMK_KC_NUM_LOCK, KEY_EN_NUMPAD_LOCK },
{ QMK_KC_KP_SLASH, KEY_EN_NUMPAD_DIVIDE },
{ QMK_KC_KP_ASTERISK, KEY_EN_NUMPAD_TIMES },
{ QMK_KC_KP_MINUS, KEY_EN_NUMPAD_MINUS },
{ QMK_KC_KP_PLUS, KEY_EN_NUMPAD_PLUS },
{ QMK_KC_KP_ENTER, KEY_EN_NUMPAD_ENTER },
{ QMK_KC_KP_1, KEY_EN_NUMPAD_1 },
{ QMK_KC_KP_2, KEY_EN_NUMPAD_2 },
{ QMK_KC_KP_3, KEY_EN_NUMPAD_3 },
{ QMK_KC_KP_4, KEY_EN_NUMPAD_4 },
{ QMK_KC_KP_5, KEY_EN_NUMPAD_5 },
{ QMK_KC_KP_6, KEY_EN_NUMPAD_6 },
{ QMK_KC_KP_7, KEY_EN_NUMPAD_7 },
{ QMK_KC_KP_8, KEY_EN_NUMPAD_8 },
{ QMK_KC_KP_9, KEY_EN_NUMPAD_9 },
{ QMK_KC_KP_0, KEY_EN_NUMPAD_0 },
{ QMK_KC_KP_DOT, KEY_EN_NUMPAD_PERIOD },
{ QMK_KC_NONUS_BACKSLASH, KEY_EN_ISO_BACK_SLASH },
{ QMK_KC_APPLICATION, KEY_EN_MENU },
{ QMK_KC_F13, "Key: F13" },
{ QMK_KC_F14, "Key: F14" },
{ QMK_KC_F15, "Key: F15" },
{ QMK_KC_F16, "Key: F16" },
{ QMK_KC_F17, "Key: F17" },
{ QMK_KC_F18, "Key: F18" },
{ QMK_KC_F19, "Key: F19" },
{ QMK_KC_F20, "Key: F20" },
{ QMK_KC_F21, "Key: F21" },
{ QMK_KC_F22, "Key: F22" },
{ QMK_KC_F23, "Key: F23" },
{ QMK_KC_F24, "Key: F24" },
{ QMK_KC_AUDIO_MUTE, KEY_EN_MEDIA_MUTE },
{ QMK_KC_AUDIO_VOL_UP, KEY_EN_MEDIA_VOLUME_UP },
{ QMK_KC_AUDIO_VOL_DOWN, KEY_EN_MEDIA_VOLUME_DOWN },
{ QMK_KC_MEDIA_NEXT_TRACK, KEY_EN_MEDIA_NEXT },
{ QMK_KC_MEDIA_PREV_TRACK, KEY_EN_MEDIA_PREVIOUS },
{ QMK_KC_MEDIA_STOP, KEY_EN_MEDIA_STOP },
{ QMK_KC_MEDIA_PLAY_PAUSE, KEY_EN_MEDIA_PLAY_PAUSE },
{ QMK_KC_MEDIA_SELECT, "Key: Media Select" },
{ QMK_KC_MEDIA_EJECT, "Key: Media Eject" },
{ QMK_KC_BRIGHTNESS_UP, "Key: Brightness Up" },
{ QMK_KC_BRIGHTNESS_DOWN, "Key: Brightness Down" },
{ 196, "Key: Task Manager" }, /* From OpenRGB QMK */
{ 202, "Key: RGB Brightness Up" }, /* From OpenRGB QMK */
{ 203, "Key: RGB Brightness Down" }, /* From OpenRGB QMK */
{ 216, KEY_EN_LEFT_SHIFT }, /* Space Cadet Left Shift */
{ 217, KEY_EN_RIGHT_SHIFT }, /* Space Cadet Right Shift */
{ QMK_KC_LEFT_CTRL, KEY_EN_LEFT_CONTROL },
{ QMK_KC_LEFT_SHIFT, KEY_EN_LEFT_SHIFT },
{ QMK_KC_LEFT_ALT, KEY_EN_LEFT_ALT },
{ QMK_KC_LEFT_GUI, KEY_EN_LEFT_WINDOWS },
{ QMK_KC_RIGHT_CTRL, KEY_EN_RIGHT_CONTROL },
{ QMK_KC_RIGHT_SHIFT, KEY_EN_RIGHT_SHIFT },
{ QMK_KC_RIGHT_ALT, KEY_EN_RIGHT_ALT },
{ QMK_KC_RIGHT_GUI, KEY_EN_RIGHT_WINDOWS },
};
File diff suppressed because it is too large. Load diff
@@ -1,292 +1,292 @@
/*---------------------------------------------------------*\
| QMKOpenRGBBaseController.cpp |
| |
| Common Driver for OpenRGB QMK Keyboard Protocol |
| |
| ChrisM 20 Dec 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "LogManager.h"
#include "RGBControllerKeyNames.h"
#include "SettingsManager.h"
#include "QMKOpenRGBBaseController.h"
using namespace std::chrono_literals;
QMKOpenRGBBaseController::QMKOpenRGBBaseController(hid_device *dev_handle, const char *path, unsigned char max_led_count)
{
/*-------------------------------------------------*\
| Get QMKOpenRGB settings |
\*-------------------------------------------------*/
json qmk_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("QMKOpenRGBDevices");
if(qmk_settings.contains("leds_per_update"))
{
if(qmk_settings["leds_per_update"] > max_led_count)
{
qmk_settings["leds_per_update"] = max_led_count;
}
else if(qmk_settings["leds_per_update"] < 1)
{
qmk_settings["leds_per_update"] = 1;
}
SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager();
settings_manager->SetSettings("QMKOpenRGBDevices", qmk_settings);
settings_manager->SaveSettings();
leds_per_update = qmk_settings["leds_per_update"];
}
else
{
leds_per_update = max_led_count;
}
if(qmk_settings.contains("delay"))
{
delay = (unsigned int)qmk_settings["delay"] * 1ms;
}
else
{
delay = 0ms;
}
dev = dev_handle;
location = path;
GetDeviceInfo();
GetModeInfo();
}
QMKOpenRGBBaseController::~QMKOpenRGBBaseController()
{
hid_close(dev);
}
std::string QMKOpenRGBBaseController::GetLocation()
{
return("HID: " + location);
}
std::string QMKOpenRGBBaseController::GetDeviceName()
{
return device_name;
}
std::string QMKOpenRGBBaseController::GetDeviceVendor()
{
return device_vendor;
}
unsigned int QMKOpenRGBBaseController::GetTotalNumberOfLEDs()
{
return total_number_of_leds;
}
unsigned int QMKOpenRGBBaseController::GetTotalNumberOfLEDsWithEmptySpace()
{
return total_number_of_leds_with_empty_space;
}
unsigned int QMKOpenRGBBaseController::GetMode()
{
return mode;
}
unsigned int QMKOpenRGBBaseController::GetModeSpeed()
{
return mode_speed;
}
unsigned int QMKOpenRGBBaseController::GetModeColor()
{
return mode_color;
}
std::vector<point_t> QMKOpenRGBBaseController::GetLEDPoints()
{
return led_points;
}
std::vector<unsigned int> QMKOpenRGBBaseController::GetLEDFlags()
{
return led_flags;
}
std::vector<std::string> QMKOpenRGBBaseController::GetLEDNames()
{
return led_names;
}
std::vector<RGBColor> QMKOpenRGBBaseController::GetLEDColors()
{
return led_colors;
}
unsigned int QMKOpenRGBBaseController::GetProtocolVersion()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_PROTOCOL_VERSION;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
return usb_buf[1];
}
std::string QMKOpenRGBBaseController::GetQMKVersion()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_QMK_VERSION;
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
hid_read(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
std::string qmk_version;
int i = 1;
while (usb_buf[i] != 0)
{
qmk_version.push_back(usb_buf[i]);
i++;
}
return qmk_version;
}
void QMKOpenRGBBaseController::GetDeviceInfo()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_DEVICE_INFO;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
total_number_of_leds = usb_buf[QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_BYTE];
total_number_of_leds_with_empty_space = usb_buf[QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE];
int i = QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE + 1;
while (usb_buf[i] != 0)
{
device_name.push_back(usb_buf[i]);
i++;
}
i++;
while (usb_buf[i] != 0)
{
device_vendor.push_back(usb_buf[i]);
i++;
}
}
void QMKOpenRGBBaseController::GetModeInfo()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_MODE_INFO;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, 65);
bytes_read = hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
mode = usb_buf[QMK_OPENRGB_MODE_BYTE];
mode_speed = usb_buf[QMK_OPENRGB_SPEED_BYTE];
/*-----------------------------------------------------*\
| QMK hue range is between 0-255 so hue needs to be |
| converted |
\*-----------------------------------------------------*/
unsigned int oldRange = 255;
unsigned int newRange = 359;
unsigned int convertedHue = (usb_buf[QMK_OPENRGB_HUE_BYTE] * newRange / oldRange);
hsv_t hsv;
hsv.hue = convertedHue;
hsv.saturation = usb_buf[QMK_OPENRGB_SATURATION_BYTE];
hsv.value = usb_buf[QMK_OPENRGB_VALUE_BYTE];
mode_color = hsv2rgb(&hsv);
}
void QMKOpenRGBBaseController::SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed)
{
SetMode(hsv_color, mode, speed, false);
}
void QMKOpenRGBBaseController::SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed, bool save)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_SET_MODE;
usb_buf[0x02] = hsv_color.hue * 255 / 359;
usb_buf[0x03] = hsv_color.saturation;
usb_buf[0x04] = hsv_color.value;
usb_buf[0x05] = mode;
usb_buf[0x06] = speed;
usb_buf[0x07] = save;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT);
}
/*---------------------------------------------------------*\
| QMKOpenRGBBaseController.cpp |
| |
| Common Driver for OpenRGB QMK Keyboard Protocol |
| |
| ChrisM 20 Dec 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "LogManager.h"
#include "RGBControllerKeyNames.h"
#include "SettingsManager.h"
#include "QMKOpenRGBBaseController.h"
using namespace std::chrono_literals;
QMKOpenRGBBaseController::QMKOpenRGBBaseController(hid_device *dev_handle, const char *path, unsigned char max_led_count)
{
/*-------------------------------------------------*\
| Get QMKOpenRGB settings |
\*-------------------------------------------------*/
json qmk_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("QMKOpenRGBDevices");
if(qmk_settings.contains("leds_per_update"))
{
if(qmk_settings["leds_per_update"] > max_led_count)
{
qmk_settings["leds_per_update"] = max_led_count;
}
else if(qmk_settings["leds_per_update"] < 1)
{
qmk_settings["leds_per_update"] = 1;
}
SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager();
settings_manager->SetSettings("QMKOpenRGBDevices", qmk_settings);
settings_manager->SaveSettings();
leds_per_update = qmk_settings["leds_per_update"];
}
else
{
leds_per_update = max_led_count;
}
if(qmk_settings.contains("delay"))
{
delay = (unsigned int)qmk_settings["delay"] * 1ms;
}
else
{
delay = 0ms;
}
dev = dev_handle;
location = path;
GetDeviceInfo();
GetModeInfo();
}
QMKOpenRGBBaseController::~QMKOpenRGBBaseController()
{
hid_close(dev);
}
std::string QMKOpenRGBBaseController::GetLocation()
{
return("HID: " + location);
}
std::string QMKOpenRGBBaseController::GetDeviceName()
{
return device_name;
}
std::string QMKOpenRGBBaseController::GetDeviceVendor()
{
return device_vendor;
}
unsigned int QMKOpenRGBBaseController::GetTotalNumberOfLEDs()
{
return total_number_of_leds;
}
unsigned int QMKOpenRGBBaseController::GetTotalNumberOfLEDsWithEmptySpace()
{
return total_number_of_leds_with_empty_space;
}
unsigned int QMKOpenRGBBaseController::GetMode()
{
return mode;
}
unsigned int QMKOpenRGBBaseController::GetModeSpeed()
{
return mode_speed;
}
unsigned int QMKOpenRGBBaseController::GetModeColor()
{
return mode_color;
}
std::vector<point_t> QMKOpenRGBBaseController::GetLEDPoints()
{
return led_points;
}
std::vector<unsigned int> QMKOpenRGBBaseController::GetLEDFlags()
{
return led_flags;
}
std::vector<std::string> QMKOpenRGBBaseController::GetLEDNames()
{
return led_names;
}
std::vector<RGBColor> QMKOpenRGBBaseController::GetLEDColors()
{
return led_colors;
}
unsigned int QMKOpenRGBBaseController::GetProtocolVersion()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_PROTOCOL_VERSION;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
return usb_buf[1];
}
std::string QMKOpenRGBBaseController::GetQMKVersion()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_QMK_VERSION;
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
hid_read(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
std::string qmk_version;
int i = 1;
while (usb_buf[i] != 0)
{
qmk_version.push_back(usb_buf[i]);
i++;
}
return qmk_version;
}
void QMKOpenRGBBaseController::GetDeviceInfo()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_DEVICE_INFO;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
total_number_of_leds = usb_buf[QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_BYTE];
total_number_of_leds_with_empty_space = usb_buf[QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE];
int i = QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE + 1;
while (usb_buf[i] != 0)
{
device_name.push_back(usb_buf[i]);
i++;
}
i++;
while (usb_buf[i] != 0)
{
device_vendor.push_back(usb_buf[i]);
i++;
}
}
void QMKOpenRGBBaseController::GetModeInfo()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_MODE_INFO;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, 65);
bytes_read = hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
mode = usb_buf[QMK_OPENRGB_MODE_BYTE];
mode_speed = usb_buf[QMK_OPENRGB_SPEED_BYTE];
/*-----------------------------------------------------*\
| QMK hue range is between 0-255 so hue needs to be |
| converted |
\*-----------------------------------------------------*/
unsigned int oldRange = 255;
unsigned int newRange = 359;
unsigned int convertedHue = (usb_buf[QMK_OPENRGB_HUE_BYTE] * newRange / oldRange);
hsv_t hsv;
hsv.hue = convertedHue;
hsv.saturation = usb_buf[QMK_OPENRGB_SATURATION_BYTE];
hsv.value = usb_buf[QMK_OPENRGB_VALUE_BYTE];
mode_color = hsv2rgb(&hsv);
}
void QMKOpenRGBBaseController::SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed)
{
SetMode(hsv_color, mode, speed, false);
}
void QMKOpenRGBBaseController::SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed, bool save)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_SET_MODE;
usb_buf[0x02] = hsv_color.hue * 255 / 359;
usb_buf[0x03] = hsv_color.saturation;
usb_buf[0x04] = hsv_color.value;
usb_buf[0x05] = mode;
usb_buf[0x06] = speed;
usb_buf[0x07] = save;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT);
}
@@ -1,78 +1,78 @@
/*---------------------------------------------------------*\
| QMKOpenRGBBaseController.h |
| |
| Common Driver for OpenRGB QMK Keyboard Protocol |
| |
| ChrisM 20 Dec 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "LogManager.h"
#include "RGBController.h"
#include "RGBControllerKeyNames.h"
#include "SettingsManager.h"
#include "QMKOpenRGBController.h"
class QMKOpenRGBBaseController
{
public:
QMKOpenRGBBaseController(hid_device *dev_handle, const char *path, unsigned char max_led_count);
virtual ~QMKOpenRGBBaseController();
std::string GetLocation();
std::string GetDeviceName();
std::string GetDeviceVendor();
unsigned int GetTotalNumberOfLEDs();
unsigned int GetTotalNumberOfLEDsWithEmptySpace();
unsigned int GetMode();
unsigned int GetModeSpeed();
unsigned int GetModeColor();
std::vector<point_t> GetLEDPoints();
std::vector<unsigned int> GetLEDFlags();
std::vector<std::string> GetLEDNames();
std::vector<RGBColor> GetLEDColors();
unsigned int GetProtocolVersion();
std::string GetQMKVersion();
void GetDeviceInfo();
void GetModeInfo();
void SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed);
void SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed, bool save);
virtual void GetLEDInfo(unsigned int leds_count) = 0;
virtual void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) = 0;
virtual void DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int leds_count) = 0;
protected:
hid_device *dev;
unsigned int leds_per_update;
std::string location;
std::string device_name;
std::string device_vendor;
std::chrono::milliseconds delay;
unsigned int total_number_of_leds;
unsigned int total_number_of_leds_with_empty_space;
unsigned int mode;
unsigned int mode_speed;
RGBColor mode_color;
std::vector<point_t> led_points;
std::vector<unsigned int> led_flags;
std::vector<std::string> led_names;
std::vector<RGBColor> led_colors;
private:
};
/*---------------------------------------------------------*\
| QMKOpenRGBBaseController.h |
| |
| Common Driver for OpenRGB QMK Keyboard Protocol |
| |
| ChrisM 20 Dec 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "LogManager.h"
#include "RGBController.h"
#include "RGBControllerKeyNames.h"
#include "SettingsManager.h"
#include "QMKOpenRGBController.h"
class QMKOpenRGBBaseController
{
public:
QMKOpenRGBBaseController(hid_device *dev_handle, const char *path, unsigned char max_led_count);
virtual ~QMKOpenRGBBaseController();
std::string GetLocation();
std::string GetDeviceName();
std::string GetDeviceVendor();
unsigned int GetTotalNumberOfLEDs();
unsigned int GetTotalNumberOfLEDsWithEmptySpace();
unsigned int GetMode();
unsigned int GetModeSpeed();
unsigned int GetModeColor();
std::vector<point_t> GetLEDPoints();
std::vector<unsigned int> GetLEDFlags();
std::vector<std::string> GetLEDNames();
std::vector<RGBColor> GetLEDColors();
unsigned int GetProtocolVersion();
std::string GetQMKVersion();
void GetDeviceInfo();
void GetModeInfo();
void SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed);
void SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed, bool save);
virtual void GetLEDInfo(unsigned int leds_count) = 0;
virtual void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) = 0;
virtual void DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int leds_count) = 0;
protected:
hid_device *dev;
unsigned int leds_per_update;
std::string location;
std::string device_name;
std::string device_vendor;
std::chrono::milliseconds delay;
unsigned int total_number_of_leds;
unsigned int total_number_of_leds_with_empty_space;
unsigned int mode;
unsigned int mode_speed;
RGBColor mode_color;
std::vector<point_t> led_points;
std::vector<unsigned int> led_flags;
std::vector<std::string> led_names;
std::vector<RGBColor> led_colors;
private:
};
@@ -1,133 +1,133 @@
/*---------------------------------------------------------*\
| QMKOpenRGBController.h |
| |
| Driver for OpenRGB QMK Keyboard Protocol |
| |
| Kasper 10 Oct 2020 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <cstring>
#include <map>
#include <hidapi.h>
#include "ResourceManager.h"
#include "RGBController.h"
#include "hsv.h"
#define QMK_OPENRGB_PACKET_SIZE 65
#define QMK_OPENRGB_HID_READ_TIMEOUT 50
enum CommandsId
{
QMK_OPENRGB_GET_PROTOCOL_VERSION = 1,
QMK_OPENRGB_GET_QMK_VERSION,
QMK_OPENRGB_GET_DEVICE_INFO,
QMK_OPENRGB_GET_MODE_INFO,
QMK_OPENRGB_GET_LED_INFO,
QMK_OPENRGB_GET_IS_MODE_ENABLED,
QMK_OPENRGB_GET_ENABLED_MODES = QMK_OPENRGB_GET_IS_MODE_ENABLED,
QMK_OPENRGB_SET_MODE,
QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED,
QMK_OPENRGB_DIRECT_MODE_SET_LEDS,
};
enum Modes
{
QMK_OPENRGB_MODE_OPENRGB_DIRECT = 1,
QMK_OPENRGB_MODE_SOLID_COLOR,
QMK_OPENRGB_MODE_ALPHA_MOD,
QMK_OPENRGB_MODE_GRADIENT_UP_DOWN,
QMK_OPENRGB_MODE_GRADIENT_LEFT_RIGHT,
QMK_OPENRGB_MODE_BREATHING,
QMK_OPENRGB_MODE_BAND_SAT,
QMK_OPENRGB_MODE_BAND_VAL,
QMK_OPENRGB_MODE_BAND_PINWHEEL_SAT,
QMK_OPENRGB_MODE_BAND_PINWHEEL_VAL,
QMK_OPENRGB_MODE_BAND_SPIRAL_SAT,
QMK_OPENRGB_MODE_BAND_SPIRAL_VAL,
QMK_OPENRGB_MODE_CYCLE_ALL,
QMK_OPENRGB_MODE_CYCLE_LEFT_RIGHT,
QMK_OPENRGB_MODE_CYCLE_UP_DOWN,
QMK_OPENRGB_MODE_CYCLE_OUT_IN,
QMK_OPENRGB_MODE_CYCLE_OUT_IN_DUAL,
QMK_OPENRGB_MODE_RAINBOW_MOVING_CHEVRON,
QMK_OPENRGB_MODE_CYCLE_PINWHEEL,
QMK_OPENRGB_MODE_CYCLE_SPIRAL,
QMK_OPENRGB_MODE_DUAL_BEACON,
QMK_OPENRGB_MODE_RAINBOW_BEACON,
QMK_OPENRGB_MODE_RAINBOW_PINWHEELS,
QMK_OPENRGB_MODE_RAINDROPS,
QMK_OPENRGB_MODE_JELLYBEAN_RAINDROPS,
QMK_OPENRGB_MODE_HUE_BREATHING,
QMK_OPENRGB_MODE_HUE_PENDULUM,
QMK_OPENRGB_MODE_HUE_WAVE,
QMK_OPENRGB_MODE_TYPING_HEATMAP,
QMK_OPENRGB_MODE_DIGITAL_RAIN,
QMK_OPENRGB_MODE_SOLID_REACTIVE_SIMPLE,
QMK_OPENRGB_MODE_SOLID_REACTIVE,
QMK_OPENRGB_MODE_SOLID_REACTIVE_WIDE,
QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTIWIDE,
QMK_OPENRGB_MODE_SOLID_REACTIVE_CROSS,
QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTICROSS,
QMK_OPENRGB_MODE_SOLID_REACTIVE_NEXUS,
QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTINEXUS,
QMK_OPENRGB_MODE_SPLASH,
QMK_OPENRGB_MODE_MULTISPLASH,
QMK_OPENRGB_MODE_SOLID_SPLASH,
QMK_OPENRGB_MODE_SOLID_MULTISPLASH,
QMK_OPENRGB_MODE_PIXEL_RAIN,
QMK_OPENRGB_MODE_PIXEL_FLOW,
QMK_OPENRGB_MODE_PIXEL_FRACTAL,
};
enum SpeedCommands
{
QMK_OPENRGB_SPEED_SLOWEST = 0x00, /* Slowest speed */
QMK_OPENRGB_SPEED_NORMAL = 0x7F, /* Normal speed */
QMK_OPENRGB_SPEED_FASTEST = 0xFF, /* Fastest speed */
};
enum
{
QMK_OPENRGB_FAILURE = 25, /* Failure status code */
QMK_OPENRGB_SUCCESS = 50, /* Success status code */
QMK_OPENRGB_END_OF_MESSAGE = 100, /* End of Message status code */
};
enum
{
QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_BYTE = 1,
QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE = 2
};
enum
{
QMK_OPENRGB_MODE_BYTE = 1,
QMK_OPENRGB_SPEED_BYTE = 2,
QMK_OPENRGB_HUE_BYTE = 3,
QMK_OPENRGB_SATURATION_BYTE = 4,
QMK_OPENRGB_VALUE_BYTE = 5,
};
enum
{
QMK_OPENRGB_POINT_X_BYTE = 1,
QMK_OPENRGB_POINT_Y_BYTE = 2,
QMK_OPENRGB_FLAG_BYTE = 3,
QMK_OPENRGB_R_COLOR_BYTE = 4,
QMK_OPENRGB_G_COLOR_BYTE = 5,
QMK_OPENRGB_B_COLOR_BYTE = 6,
QMK_OPENRGB_KEYCODE_BYTE = 7
};
typedef struct
{
uint8_t x;
uint8_t y;
} point_t;
/*---------------------------------------------------------*\
| QMKOpenRGBController.h |
| |
| Driver for OpenRGB QMK Keyboard Protocol |
| |
| Kasper 10 Oct 2020 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <cstring>
#include <map>
#include <hidapi.h>
#include "ResourceManager.h"
#include "RGBController.h"
#include "hsv.h"
#define QMK_OPENRGB_PACKET_SIZE 65
#define QMK_OPENRGB_HID_READ_TIMEOUT 50
enum CommandsId
{
QMK_OPENRGB_GET_PROTOCOL_VERSION = 1,
QMK_OPENRGB_GET_QMK_VERSION,
QMK_OPENRGB_GET_DEVICE_INFO,
QMK_OPENRGB_GET_MODE_INFO,
QMK_OPENRGB_GET_LED_INFO,
QMK_OPENRGB_GET_IS_MODE_ENABLED,
QMK_OPENRGB_GET_ENABLED_MODES = QMK_OPENRGB_GET_IS_MODE_ENABLED,
QMK_OPENRGB_SET_MODE,
QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED,
QMK_OPENRGB_DIRECT_MODE_SET_LEDS,
};
enum Modes
{
QMK_OPENRGB_MODE_OPENRGB_DIRECT = 1,
QMK_OPENRGB_MODE_SOLID_COLOR,
QMK_OPENRGB_MODE_ALPHA_MOD,
QMK_OPENRGB_MODE_GRADIENT_UP_DOWN,
QMK_OPENRGB_MODE_GRADIENT_LEFT_RIGHT,
QMK_OPENRGB_MODE_BREATHING,
QMK_OPENRGB_MODE_BAND_SAT,
QMK_OPENRGB_MODE_BAND_VAL,
QMK_OPENRGB_MODE_BAND_PINWHEEL_SAT,
QMK_OPENRGB_MODE_BAND_PINWHEEL_VAL,
QMK_OPENRGB_MODE_BAND_SPIRAL_SAT,
QMK_OPENRGB_MODE_BAND_SPIRAL_VAL,
QMK_OPENRGB_MODE_CYCLE_ALL,
QMK_OPENRGB_MODE_CYCLE_LEFT_RIGHT,
QMK_OPENRGB_MODE_CYCLE_UP_DOWN,
QMK_OPENRGB_MODE_CYCLE_OUT_IN,
QMK_OPENRGB_MODE_CYCLE_OUT_IN_DUAL,
QMK_OPENRGB_MODE_RAINBOW_MOVING_CHEVRON,
QMK_OPENRGB_MODE_CYCLE_PINWHEEL,
QMK_OPENRGB_MODE_CYCLE_SPIRAL,
QMK_OPENRGB_MODE_DUAL_BEACON,
QMK_OPENRGB_MODE_RAINBOW_BEACON,
QMK_OPENRGB_MODE_RAINBOW_PINWHEELS,
QMK_OPENRGB_MODE_RAINDROPS,
QMK_OPENRGB_MODE_JELLYBEAN_RAINDROPS,
QMK_OPENRGB_MODE_HUE_BREATHING,
QMK_OPENRGB_MODE_HUE_PENDULUM,
QMK_OPENRGB_MODE_HUE_WAVE,
QMK_OPENRGB_MODE_TYPING_HEATMAP,
QMK_OPENRGB_MODE_DIGITAL_RAIN,
QMK_OPENRGB_MODE_SOLID_REACTIVE_SIMPLE,
QMK_OPENRGB_MODE_SOLID_REACTIVE,
QMK_OPENRGB_MODE_SOLID_REACTIVE_WIDE,
QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTIWIDE,
QMK_OPENRGB_MODE_SOLID_REACTIVE_CROSS,
QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTICROSS,
QMK_OPENRGB_MODE_SOLID_REACTIVE_NEXUS,
QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTINEXUS,
QMK_OPENRGB_MODE_SPLASH,
QMK_OPENRGB_MODE_MULTISPLASH,
QMK_OPENRGB_MODE_SOLID_SPLASH,
QMK_OPENRGB_MODE_SOLID_MULTISPLASH,
QMK_OPENRGB_MODE_PIXEL_RAIN,
QMK_OPENRGB_MODE_PIXEL_FLOW,
QMK_OPENRGB_MODE_PIXEL_FRACTAL,
};
enum SpeedCommands
{
QMK_OPENRGB_SPEED_SLOWEST = 0x00, /* Slowest speed */
QMK_OPENRGB_SPEED_NORMAL = 0x7F, /* Normal speed */
QMK_OPENRGB_SPEED_FASTEST = 0xFF, /* Fastest speed */
};
enum
{
QMK_OPENRGB_FAILURE = 25, /* Failure status code */
QMK_OPENRGB_SUCCESS = 50, /* Success status code */
QMK_OPENRGB_END_OF_MESSAGE = 100, /* End of Message status code */
};
enum
{
QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_BYTE = 1,
QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE = 2
};
enum
{
QMK_OPENRGB_MODE_BYTE = 1,
QMK_OPENRGB_SPEED_BYTE = 2,
QMK_OPENRGB_HUE_BYTE = 3,
QMK_OPENRGB_SATURATION_BYTE = 4,
QMK_OPENRGB_VALUE_BYTE = 5,
};
enum
{
QMK_OPENRGB_POINT_X_BYTE = 1,
QMK_OPENRGB_POINT_Y_BYTE = 2,
QMK_OPENRGB_FLAG_BYTE = 3,
QMK_OPENRGB_R_COLOR_BYTE = 4,
QMK_OPENRGB_G_COLOR_BYTE = 5,
QMK_OPENRGB_B_COLOR_BYTE = 6,
QMK_OPENRGB_KEYCODE_BYTE = 7
};
typedef struct
{
uint8_t x;
uint8_t y;
} point_t;
@@ -1,167 +1,167 @@
/*---------------------------------------------------------*\
| QMKOpenRGBControllerDetect.cpp |
| |
| Detector for OpenRGB QMK Keyboard Protocol |
| |
| Kasper 10 Oct 2020 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <string>
#include <hidapi.h>
#include "Detector.h"
#include "QMKOpenRGBRev9Controller.h"
#include "QMKOpenRGBRevBController.h"
#include "QMKOpenRGBRevDController.h"
#include "RGBController_QMKOpenRGBRev9.h"
#include "RGBController_QMKOpenRGBRevB.h"
#include "RGBController_QMKOpenRGBRevD.h"
#include "RGBController_QMKOpenRGBRevE.h"
#include "LogManager.h"
#include "SettingsManager.h"
/*-----------------------------------------------------*\
| Protocol version |
\*-----------------------------------------------------*/
#define QMK_OPENRGB_PROTOCOL_VERSION_9 0x09
#define QMK_OPENRGB_PROTOCOL_VERSION_B 0x0B
#define QMK_OPENRGB_PROTOCOL_VERSION_C 0x0C
#define QMK_OPENRGB_PROTOCOL_VERSION_D 0x0D
#define QMK_OPENRGB_PROTOCOL_VERSION_E 0x0E
/*-----------------------------------------------------*\
| Usage and Usage Page |
\*-----------------------------------------------------*/
#define QMK_USAGE_PAGE 0xFF60
#define QMK_USAGE 0x61
unsigned int GetProtocolVersion(hid_device *dev)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_PROTOCOL_VERSION;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
return usb_buf[1];
}
void DetectQMKOpenRGBControllers(hid_device_info *info, const std::string&)
{
hid_device *dev = hid_open_path(info->path);
if(dev)
{
/*-----------------------------------------------------*\
| Use Rev9 controller for getting protocol version. |
| Protocol version request may not change across |
| protocol versions |
\*-----------------------------------------------------*/
unsigned int version = GetProtocolVersion(dev);
switch(version)
{
case QMK_OPENRGB_PROTOCOL_VERSION_9:
{
QMKOpenRGBRev9Controller* controller = new QMKOpenRGBRev9Controller(dev, info->path);
RGBController_QMKOpenRGBRev9* rgb_controller = new RGBController_QMKOpenRGBRev9(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
case QMK_OPENRGB_PROTOCOL_VERSION_B:
{
QMKOpenRGBRevBController* controller = new QMKOpenRGBRevBController(dev, info->path);
RGBController_QMKOpenRGBRevB* rgb_controller = new RGBController_QMKOpenRGBRevB(controller, false);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
case QMK_OPENRGB_PROTOCOL_VERSION_C:
{
QMKOpenRGBRevBController* controller = new QMKOpenRGBRevBController(dev, info->path);
RGBController_QMKOpenRGBRevB* rgb_controller = new RGBController_QMKOpenRGBRevB(controller, true);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
case QMK_OPENRGB_PROTOCOL_VERSION_D:
{
QMKOpenRGBRevDController* controller = new QMKOpenRGBRevDController(dev, info->path);
RGBController_QMKOpenRGBRevD* rgb_controller = new RGBController_QMKOpenRGBRevD(controller, true);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
case QMK_OPENRGB_PROTOCOL_VERSION_E:
{
QMKOpenRGBRevDController* controller = new QMKOpenRGBRevDController(dev, info->path);
RGBController_QMKOpenRGBRevE* rgb_controller = new RGBController_QMKOpenRGBRevE(controller, true);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
default:
if (version == 0)
{
LOG_WARNING("[QMK OpenRGB] Detection failed - the detected keyboard does not have the OpenRGB protocol feature enabled! \n"
"Please make sure your keyboard supports RGB Matrix, add OPENRGB_ENABLE = yes to the rules.mk inside your keymap folder, compile and flash again!");
}
else if (version < QMK_OPENRGB_PROTOCOL_VERSION_9)
{
LOG_WARNING("[QMK OpenRGB] Detection failed - the detected keyboard is using an outdated protocol version %i. Please update to to the update to the latest version of QMK-OpenRGB! \n"
"For officaly supported QMK boards grab <a href=\"https://github.com/Kasper24/QMK-OpenRGB\">url</a> \n"
"For Sonix boards grab <a href=\"https://github.com/SonixQMK/qmk_firmware/tree/sn32_openrgb\">url</a>", version);
}
else if (version > QMK_OPENRGB_PROTOCOL_VERSION_E)
{
LOG_WARNING("[QMK OpenRGB] Detection failed - the detected keyboard is using version protocol %i which is not supported by this OpenRGB build. Please update OpenRGB!", version);
}
}
}
}
void RegisterQMKDetectors()
{
/*-------------------------------------------------*\
| Get QMKOpenRGB settings |
\*-------------------------------------------------*/
json qmk_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("QMKOpenRGBDevices");
if(qmk_settings.contains("devices"))
{
for(unsigned int device_idx = 0; device_idx < qmk_settings["devices"].size(); device_idx++)
{
if( qmk_settings["devices"][device_idx].contains("usb_pid")
&& qmk_settings["devices"][device_idx].contains("usb_vid")
&& qmk_settings["devices"][device_idx].contains("name"))
{
std::string usb_pid_str = qmk_settings["devices"][device_idx]["usb_pid"];
std::string usb_vid_str = qmk_settings["devices"][device_idx]["usb_vid"];
std::string name = qmk_settings["devices"][device_idx]["name"];
/*-------------------------------------*\
| Parse hex string to integer |
\*-------------------------------------*/
unsigned short usb_pid = std::stoi(usb_pid_str, 0, 16);
unsigned short usb_vid = std::stoi(usb_vid_str, 0, 16);
REGISTER_DYNAMIC_HID_DETECTOR_IPU(name, DetectQMKOpenRGBControllers, usb_vid, usb_pid, 1, QMK_USAGE_PAGE, QMK_USAGE);
}
}
}
}
REGISTER_DYNAMIC_DETECTOR("QMK OpenRGB Devices", RegisterQMKDetectors);
/*---------------------------------------------------------*\
| QMKOpenRGBControllerDetect.cpp |
| |
| Detector for OpenRGB QMK Keyboard Protocol |
| |
| Kasper 10 Oct 2020 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <string>
#include <hidapi.h>
#include "Detector.h"
#include "QMKOpenRGBRev9Controller.h"
#include "QMKOpenRGBRevBController.h"
#include "QMKOpenRGBRevDController.h"
#include "RGBController_QMKOpenRGBRev9.h"
#include "RGBController_QMKOpenRGBRevB.h"
#include "RGBController_QMKOpenRGBRevD.h"
#include "RGBController_QMKOpenRGBRevE.h"
#include "LogManager.h"
#include "SettingsManager.h"
/*-----------------------------------------------------*\
| Protocol version |
\*-----------------------------------------------------*/
#define QMK_OPENRGB_PROTOCOL_VERSION_9 0x09
#define QMK_OPENRGB_PROTOCOL_VERSION_B 0x0B
#define QMK_OPENRGB_PROTOCOL_VERSION_C 0x0C
#define QMK_OPENRGB_PROTOCOL_VERSION_D 0x0D
#define QMK_OPENRGB_PROTOCOL_VERSION_E 0x0E
/*-----------------------------------------------------*\
| Usage and Usage Page |
\*-----------------------------------------------------*/
#define QMK_USAGE_PAGE 0xFF60
#define QMK_USAGE 0x61
unsigned int GetProtocolVersion(hid_device *dev)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_PROTOCOL_VERSION;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
return usb_buf[1];
}
void DetectQMKOpenRGBControllers(hid_device_info *info, const std::string&)
{
hid_device *dev = hid_open_path(info->path);
if(dev)
{
/*-----------------------------------------------------*\
| Use Rev9 controller for getting protocol version. |
| Protocol version request may not change across |
| protocol versions |
\*-----------------------------------------------------*/
unsigned int version = GetProtocolVersion(dev);
switch(version)
{
case QMK_OPENRGB_PROTOCOL_VERSION_9:
{
QMKOpenRGBRev9Controller* controller = new QMKOpenRGBRev9Controller(dev, info->path);
RGBController_QMKOpenRGBRev9* rgb_controller = new RGBController_QMKOpenRGBRev9(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
case QMK_OPENRGB_PROTOCOL_VERSION_B:
{
QMKOpenRGBRevBController* controller = new QMKOpenRGBRevBController(dev, info->path);
RGBController_QMKOpenRGBRevB* rgb_controller = new RGBController_QMKOpenRGBRevB(controller, false);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
case QMK_OPENRGB_PROTOCOL_VERSION_C:
{
QMKOpenRGBRevBController* controller = new QMKOpenRGBRevBController(dev, info->path);
RGBController_QMKOpenRGBRevB* rgb_controller = new RGBController_QMKOpenRGBRevB(controller, true);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
case QMK_OPENRGB_PROTOCOL_VERSION_D:
{
QMKOpenRGBRevDController* controller = new QMKOpenRGBRevDController(dev, info->path);
RGBController_QMKOpenRGBRevD* rgb_controller = new RGBController_QMKOpenRGBRevD(controller, true);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
case QMK_OPENRGB_PROTOCOL_VERSION_E:
{
QMKOpenRGBRevDController* controller = new QMKOpenRGBRevDController(dev, info->path);
RGBController_QMKOpenRGBRevE* rgb_controller = new RGBController_QMKOpenRGBRevE(controller, true);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
default:
if (version == 0)
{
LOG_WARNING("[QMK OpenRGB] Detection failed - the detected keyboard does not have the OpenRGB protocol feature enabled! \n"
"Please make sure your keyboard supports RGB Matrix, add OPENRGB_ENABLE = yes to the rules.mk inside your keymap folder, compile and flash again!");
}
else if (version < QMK_OPENRGB_PROTOCOL_VERSION_9)
{
LOG_WARNING("[QMK OpenRGB] Detection failed - the detected keyboard is using an outdated protocol version %i. Please update to to the update to the latest version of QMK-OpenRGB! \n"
"For officaly supported QMK boards grab <a href=\"https://github.com/Kasper24/QMK-OpenRGB\">url</a> \n"
"For Sonix boards grab <a href=\"https://github.com/SonixQMK/qmk_firmware/tree/sn32_openrgb\">url</a>", version);
}
else if (version > QMK_OPENRGB_PROTOCOL_VERSION_E)
{
LOG_WARNING("[QMK OpenRGB] Detection failed - the detected keyboard is using version protocol %i which is not supported by this OpenRGB build. Please update OpenRGB!", version);
}
}
}
}
void RegisterQMKDetectors()
{
/*-------------------------------------------------*\
| Get QMKOpenRGB settings |
\*-------------------------------------------------*/
json qmk_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("QMKOpenRGBDevices");
if(qmk_settings.contains("devices"))
{
for(unsigned int device_idx = 0; device_idx < qmk_settings["devices"].size(); device_idx++)
{
if( qmk_settings["devices"][device_idx].contains("usb_pid")
&& qmk_settings["devices"][device_idx].contains("usb_vid")
&& qmk_settings["devices"][device_idx].contains("name"))
{
std::string usb_pid_str = qmk_settings["devices"][device_idx]["usb_pid"];
std::string usb_vid_str = qmk_settings["devices"][device_idx]["usb_vid"];
std::string name = qmk_settings["devices"][device_idx]["name"];
/*-------------------------------------*\
| Parse hex string to integer |
\*-------------------------------------*/
unsigned short usb_pid = std::stoi(usb_pid_str, 0, 16);
unsigned short usb_vid = std::stoi(usb_vid_str, 0, 16);
REGISTER_DYNAMIC_HID_DETECTOR_IPU(name, DetectQMKOpenRGBControllers, usb_vid, usb_pid, 1, QMK_USAGE_PAGE, QMK_USAGE);
}
}
}
}
REGISTER_DYNAMIC_DETECTOR("QMK OpenRGB Devices", RegisterQMKDetectors);
@@ -1,170 +1,170 @@
/*---------------------------------------------------------*\
| QMKOpenRGBRev9Controller.cpp |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision 9 |
| Revision 9 was initially supported by OpenRGB 0.6 |
| |
| Kasper 10 Oct 2020 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "QMKKeycodes.h"
#include "QMKOpenRGBRev9Controller.h"
using namespace std::chrono_literals;
QMKOpenRGBRev9Controller::QMKOpenRGBRev9Controller(hid_device *dev_handle, const char *path) :
QMKOpenRGBBaseController(dev_handle, path, 20)
{
}
QMKOpenRGBRev9Controller::~QMKOpenRGBRev9Controller()
{
}
void QMKOpenRGBRev9Controller::GetLEDInfo(unsigned int led)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_LED_INFO;
usb_buf[0x02] = led;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
if(usb_buf[62] != QMK_OPENRGB_FAILURE)
{
led_points.push_back(point_t{usb_buf[QMK_OPENRGB_POINT_X_BYTE], usb_buf[QMK_OPENRGB_POINT_Y_BYTE]});
led_flags.push_back(usb_buf[QMK_OPENRGB_FLAG_BYTE]);
led_colors.push_back(ToRGBColor(usb_buf[QMK_OPENRGB_R_COLOR_BYTE], usb_buf[QMK_OPENRGB_G_COLOR_BYTE], usb_buf[QMK_OPENRGB_B_COLOR_BYTE]));
}
if(usb_buf[QMK_OPENRGB_KEYCODE_BYTE] != 0)
{
if(qmk_keynames.count(usb_buf[QMK_OPENRGB_KEYCODE_BYTE]) > 0)
{
led_names.push_back(qmk_keynames[usb_buf[QMK_OPENRGB_KEYCODE_BYTE]]);
}
else
{
LOG_DEBUG("[%s] Key code: %d (%02X) @ offset %d was not found in the QMK keyname map",
device_name.c_str(), usb_buf[QMK_OPENRGB_KEYCODE_BYTE],
usb_buf[QMK_OPENRGB_KEYCODE_BYTE], led);
led_names.push_back(KEY_EN_UNUSED);
}
}
}
bool QMKOpenRGBRev9Controller::GetIsModeEnabled(unsigned int mode)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_IS_MODE_ENABLED;
usb_buf[0x02] = mode;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
return usb_buf[1] == QMK_OPENRGB_SUCCESS ? true : false;
}
void QMKOpenRGBRev9Controller::DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED;
usb_buf[0x02] = led;
usb_buf[0x03] = red;
usb_buf[0x04] = green;
usb_buf[0x05] = blue;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT);
}
void QMKOpenRGBRev9Controller::DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int leds_count)
{
unsigned int leds_sent = 0;
unsigned int tmp_leds_per_update = leds_per_update;
while (leds_sent < leds_count)
{
if ((leds_count - leds_sent) < tmp_leds_per_update)
{
tmp_leds_per_update = leds_count - leds_sent;
}
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_LEDS;
usb_buf[0x02] = leds_sent;
usb_buf[0x03] = tmp_leds_per_update;
for (unsigned int led_idx = 0; led_idx < tmp_leds_per_update; led_idx++)
{
usb_buf[(led_idx * 3) + 4] = RGBGetRValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 3) + 5] = RGBGetGValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 3) + 6] = RGBGetBValue(colors[led_idx + leds_sent]);
}
hid_write(dev, usb_buf, 65);
if(delay > 0ms)
{
std::this_thread::sleep_for(delay);
}
leds_sent += tmp_leds_per_update;
}
}
/*---------------------------------------------------------*\
| QMKOpenRGBRev9Controller.cpp |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision 9 |
| Revision 9 was initially supported by OpenRGB 0.6 |
| |
| Kasper 10 Oct 2020 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "QMKKeycodes.h"
#include "QMKOpenRGBRev9Controller.h"
using namespace std::chrono_literals;
QMKOpenRGBRev9Controller::QMKOpenRGBRev9Controller(hid_device *dev_handle, const char *path) :
QMKOpenRGBBaseController(dev_handle, path, 20)
{
}
QMKOpenRGBRev9Controller::~QMKOpenRGBRev9Controller()
{
}
void QMKOpenRGBRev9Controller::GetLEDInfo(unsigned int led)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_LED_INFO;
usb_buf[0x02] = led;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
if(usb_buf[62] != QMK_OPENRGB_FAILURE)
{
led_points.push_back(point_t{usb_buf[QMK_OPENRGB_POINT_X_BYTE], usb_buf[QMK_OPENRGB_POINT_Y_BYTE]});
led_flags.push_back(usb_buf[QMK_OPENRGB_FLAG_BYTE]);
led_colors.push_back(ToRGBColor(usb_buf[QMK_OPENRGB_R_COLOR_BYTE], usb_buf[QMK_OPENRGB_G_COLOR_BYTE], usb_buf[QMK_OPENRGB_B_COLOR_BYTE]));
}
if(usb_buf[QMK_OPENRGB_KEYCODE_BYTE] != 0)
{
if(qmk_keynames.count(usb_buf[QMK_OPENRGB_KEYCODE_BYTE]) > 0)
{
led_names.push_back(qmk_keynames[usb_buf[QMK_OPENRGB_KEYCODE_BYTE]]);
}
else
{
LOG_DEBUG("[%s] Key code: %d (%02X) @ offset %d was not found in the QMK keyname map",
device_name.c_str(), usb_buf[QMK_OPENRGB_KEYCODE_BYTE],
usb_buf[QMK_OPENRGB_KEYCODE_BYTE], led);
led_names.push_back(KEY_EN_UNUSED);
}
}
}
bool QMKOpenRGBRev9Controller::GetIsModeEnabled(unsigned int mode)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_IS_MODE_ENABLED;
usb_buf[0x02] = mode;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
return usb_buf[1] == QMK_OPENRGB_SUCCESS ? true : false;
}
void QMKOpenRGBRev9Controller::DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED;
usb_buf[0x02] = led;
usb_buf[0x03] = red;
usb_buf[0x04] = green;
usb_buf[0x05] = blue;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT);
}
void QMKOpenRGBRev9Controller::DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int leds_count)
{
unsigned int leds_sent = 0;
unsigned int tmp_leds_per_update = leds_per_update;
while (leds_sent < leds_count)
{
if ((leds_count - leds_sent) < tmp_leds_per_update)
{
tmp_leds_per_update = leds_count - leds_sent;
}
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_LEDS;
usb_buf[0x02] = leds_sent;
usb_buf[0x03] = tmp_leds_per_update;
for (unsigned int led_idx = 0; led_idx < tmp_leds_per_update; led_idx++)
{
usb_buf[(led_idx * 3) + 4] = RGBGetRValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 3) + 5] = RGBGetGValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 3) + 6] = RGBGetBValue(colors[led_idx + leds_sent]);
}
hid_write(dev, usb_buf, 65);
if(delay > 0ms)
{
std::this_thread::sleep_for(delay);
}
leds_sent += tmp_leds_per_update;
}
}
@@ -1,33 +1,33 @@
/*---------------------------------------------------------*\
| QMKOpenRGBRev9Controller.h |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision 9 |
| Revision 9 was initially supported by OpenRGB 0.6 |
| |
| Kasper 10 Oct 2020 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "QMKOpenRGBBaseController.h"
class QMKOpenRGBRev9Controller : public QMKOpenRGBBaseController
{
public:
QMKOpenRGBRev9Controller(hid_device *dev_handle, const char *path);
~QMKOpenRGBRev9Controller();
//Virtual function implementations
void GetLEDInfo(unsigned int led);
void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue);
void DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int num_colors);
//Protocol Specific functions
bool GetIsModeEnabled(unsigned int mode);
private:
};
/*---------------------------------------------------------*\
| QMKOpenRGBRev9Controller.h |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision 9 |
| Revision 9 was initially supported by OpenRGB 0.6 |
| |
| Kasper 10 Oct 2020 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "QMKOpenRGBBaseController.h"
class QMKOpenRGBRev9Controller : public QMKOpenRGBBaseController
{
public:
QMKOpenRGBRev9Controller(hid_device *dev_handle, const char *path);
~QMKOpenRGBRev9Controller();
//Virtual function implementations
void GetLEDInfo(unsigned int led);
void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue);
void DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int num_colors);
//Protocol Specific functions
bool GetIsModeEnabled(unsigned int mode);
private:
};
@@ -1,99 +1,99 @@
/*---------------------------------------------------------*\
| RGBController_QMKOpenRGBRev9.h |
| |
| RGBController for OpenRGB QMK Keyboard Protocol |
| Revision 9 |
| |
| Kasper 10 Oct 2020 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include "RGBController.h"
#include "QMKOpenRGBRev9Controller.h"
#define NO_LED 0xFFFFFFFF
typedef std::vector<std::vector<unsigned int>> VectorMatrix;
class RGBController_QMKOpenRGBRev9 : public RGBController
{
public:
RGBController_QMKOpenRGBRev9(QMKOpenRGBRev9Controller* controller_ptr);
~RGBController_QMKOpenRGBRev9();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
QMKOpenRGBRev9Controller* controller;
std::vector<unsigned int> flat_matrix_map;
std::vector<unsigned int> flat_underglow_map;
void InitializeMode
(
std::string name,
unsigned int &current_mode,
unsigned int flags,
unsigned int color_mode
);
unsigned int CalculateDivisor
(
std::vector<point_t> led_points,
std::set<int> rows,
std::set<int> columns
);
void CountKeyTypes
(
std::vector<unsigned int> led_flags,
unsigned int total_led_count,
unsigned int& key_leds,
unsigned int& underglow_leds
);
void PlaceLEDsInMaps
(
std::set<int> unique_rows,
std::set<int> unique_cols,
unsigned int divisor,
std::vector<point_t> led_points,
std::vector<unsigned int> led_flags,
VectorMatrix& matrix_map_xl,
VectorMatrix& underglow_map_xl
);
VectorMatrix MakeEmptyMatrixMap
(
std::size_t height,
std::size_t width
);
void CleanMatrixMaps
(
VectorMatrix& matrix_map,
VectorMatrix& underglow_map,
unsigned int height,
bool has_underglow
);
std::vector<unsigned int> FlattenMatrixMap
(
VectorMatrix matrix_map
);
};
/*---------------------------------------------------------*\
| RGBController_QMKOpenRGBRev9.h |
| |
| RGBController for OpenRGB QMK Keyboard Protocol |
| Revision 9 |
| |
| Kasper 10 Oct 2020 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include "RGBController.h"
#include "QMKOpenRGBRev9Controller.h"
#define NO_LED 0xFFFFFFFF
typedef std::vector<std::vector<unsigned int>> VectorMatrix;
class RGBController_QMKOpenRGBRev9 : public RGBController
{
public:
RGBController_QMKOpenRGBRev9(QMKOpenRGBRev9Controller* controller_ptr);
~RGBController_QMKOpenRGBRev9();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
QMKOpenRGBRev9Controller* controller;
std::vector<unsigned int> flat_matrix_map;
std::vector<unsigned int> flat_underglow_map;
void InitializeMode
(
std::string name,
unsigned int &current_mode,
unsigned int flags,
unsigned int color_mode
);
unsigned int CalculateDivisor
(
std::vector<point_t> led_points,
std::set<int> rows,
std::set<int> columns
);
void CountKeyTypes
(
std::vector<unsigned int> led_flags,
unsigned int total_led_count,
unsigned int& key_leds,
unsigned int& underglow_leds
);
void PlaceLEDsInMaps
(
std::set<int> unique_rows,
std::set<int> unique_cols,
unsigned int divisor,
std::vector<point_t> led_points,
std::vector<unsigned int> led_flags,
VectorMatrix& matrix_map_xl,
VectorMatrix& underglow_map_xl
);
VectorMatrix MakeEmptyMatrixMap
(
std::size_t height,
std::size_t width
);
void CleanMatrixMaps
(
VectorMatrix& matrix_map,
VectorMatrix& underglow_map,
unsigned int height,
bool has_underglow
);
std::vector<unsigned int> FlattenMatrixMap
(
VectorMatrix matrix_map
);
};
@@ -1,194 +1,194 @@
/*---------------------------------------------------------*\
| QMKOpenRGBRevBController.cpp |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision B |
| |
| Kasper 28 Jun 2021 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "QMKKeycodes.h"
#include "QMKOpenRGBRevBController.h"
using namespace std::chrono_literals;
QMKOpenRGBRevBController::QMKOpenRGBRevBController(hid_device *dev_handle, const char *path) :
QMKOpenRGBBaseController(dev_handle, path, 20)
{
}
QMKOpenRGBRevBController::~QMKOpenRGBRevBController()
{
}
void QMKOpenRGBRevBController::GetLEDInfo(unsigned int leds_count)
{
unsigned int leds_sent = 0;
unsigned int leds_per_update_info = 8;
while (leds_sent < leds_count)
{
if ((leds_count - leds_sent) < leds_per_update_info)
{
leds_per_update_info = leds_count - leds_sent;
}
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_LED_INFO;
usb_buf[0x02] = leds_sent;
usb_buf[0x03] = leds_per_update_info;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
for (unsigned int led_idx = 0; led_idx < leds_per_update_info; led_idx++)
{
unsigned int offset = led_idx * 7;
if(usb_buf[(offset) + QMK_OPENRGB_FLAG_BYTE] != QMK_OPENRGB_FAILURE)
{
led_points.push_back(point_t{usb_buf[(offset) + QMK_OPENRGB_POINT_X_BYTE], usb_buf[(offset) + QMK_OPENRGB_POINT_Y_BYTE]});
led_flags.push_back(usb_buf[(offset) + QMK_OPENRGB_FLAG_BYTE]);
led_colors.push_back(ToRGBColor(usb_buf[(offset) + QMK_OPENRGB_R_COLOR_BYTE], usb_buf[(offset) + QMK_OPENRGB_G_COLOR_BYTE], usb_buf[(offset) + QMK_OPENRGB_B_COLOR_BYTE]));
}
if(usb_buf[(offset) + QMK_OPENRGB_KEYCODE_BYTE] != 0)
{
if(qmk_keynames.count(usb_buf[(offset) + QMK_OPENRGB_KEYCODE_BYTE]) > 0)
{
led_names.push_back(qmk_keynames[usb_buf[(offset) + QMK_OPENRGB_KEYCODE_BYTE]]);
}
else
{
LOG_DEBUG("[%s] Key code: %d (%02X) @ offset %d was not found in the QMK keyname map",
device_name.c_str(), usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE],
usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE], leds_sent + led_idx);
led_names.push_back(KEY_EN_UNUSED);
}
}
}
leds_sent += leds_per_update_info;
}
}
std::vector<unsigned int> QMKOpenRGBRevBController::GetEnabledModes()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_ENABLED_MODES;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
std::vector<unsigned int> enabled_modes;
int i = 1;
while (usb_buf[i] != 0)
{
enabled_modes.push_back(usb_buf[i]);
i++;
}
return enabled_modes;
}
void QMKOpenRGBRevBController::DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED;
usb_buf[0x02] = led;
usb_buf[0x03] = red;
usb_buf[0x04] = green;
usb_buf[0x05] = blue;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT);
}
void QMKOpenRGBRevBController::DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int leds_count)
{
unsigned int leds_sent = 0;
unsigned int tmp_leds_per_update = leds_per_update;
while (leds_sent < leds_count)
{
if ((leds_count - leds_sent) < tmp_leds_per_update)
{
tmp_leds_per_update = leds_count - leds_sent;
}
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_LEDS;
usb_buf[0x02] = leds_sent;
usb_buf[0x03] = tmp_leds_per_update;
for (unsigned int led_idx = 0; led_idx < tmp_leds_per_update; led_idx++)
{
usb_buf[(led_idx * 3) + 4] = RGBGetRValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 3) + 5] = RGBGetGValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 3) + 6] = RGBGetBValue(colors[led_idx + leds_sent]);
}
hid_write(dev, usb_buf, 65);
if(delay > 0ms)
{
std::this_thread::sleep_for(delay);
}
leds_sent += tmp_leds_per_update;
}
}
/*---------------------------------------------------------*\
| QMKOpenRGBRevBController.cpp |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision B |
| |
| Kasper 28 Jun 2021 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "QMKKeycodes.h"
#include "QMKOpenRGBRevBController.h"
using namespace std::chrono_literals;
QMKOpenRGBRevBController::QMKOpenRGBRevBController(hid_device *dev_handle, const char *path) :
QMKOpenRGBBaseController(dev_handle, path, 20)
{
}
QMKOpenRGBRevBController::~QMKOpenRGBRevBController()
{
}
void QMKOpenRGBRevBController::GetLEDInfo(unsigned int leds_count)
{
unsigned int leds_sent = 0;
unsigned int leds_per_update_info = 8;
while (leds_sent < leds_count)
{
if ((leds_count - leds_sent) < leds_per_update_info)
{
leds_per_update_info = leds_count - leds_sent;
}
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_LED_INFO;
usb_buf[0x02] = leds_sent;
usb_buf[0x03] = leds_per_update_info;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
for (unsigned int led_idx = 0; led_idx < leds_per_update_info; led_idx++)
{
unsigned int offset = led_idx * 7;
if(usb_buf[(offset) + QMK_OPENRGB_FLAG_BYTE] != QMK_OPENRGB_FAILURE)
{
led_points.push_back(point_t{usb_buf[(offset) + QMK_OPENRGB_POINT_X_BYTE], usb_buf[(offset) + QMK_OPENRGB_POINT_Y_BYTE]});
led_flags.push_back(usb_buf[(offset) + QMK_OPENRGB_FLAG_BYTE]);
led_colors.push_back(ToRGBColor(usb_buf[(offset) + QMK_OPENRGB_R_COLOR_BYTE], usb_buf[(offset) + QMK_OPENRGB_G_COLOR_BYTE], usb_buf[(offset) + QMK_OPENRGB_B_COLOR_BYTE]));
}
if(usb_buf[(offset) + QMK_OPENRGB_KEYCODE_BYTE] != 0)
{
if(qmk_keynames.count(usb_buf[(offset) + QMK_OPENRGB_KEYCODE_BYTE]) > 0)
{
led_names.push_back(qmk_keynames[usb_buf[(offset) + QMK_OPENRGB_KEYCODE_BYTE]]);
}
else
{
LOG_DEBUG("[%s] Key code: %d (%02X) @ offset %d was not found in the QMK keyname map",
device_name.c_str(), usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE],
usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE], leds_sent + led_idx);
led_names.push_back(KEY_EN_UNUSED);
}
}
}
leds_sent += leds_per_update_info;
}
}
std::vector<unsigned int> QMKOpenRGBRevBController::GetEnabledModes()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_ENABLED_MODES;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
std::vector<unsigned int> enabled_modes;
int i = 1;
while (usb_buf[i] != 0)
{
enabled_modes.push_back(usb_buf[i]);
i++;
}
return enabled_modes;
}
void QMKOpenRGBRevBController::DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED;
usb_buf[0x02] = led;
usb_buf[0x03] = red;
usb_buf[0x04] = green;
usb_buf[0x05] = blue;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT);
}
void QMKOpenRGBRevBController::DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int leds_count)
{
unsigned int leds_sent = 0;
unsigned int tmp_leds_per_update = leds_per_update;
while (leds_sent < leds_count)
{
if ((leds_count - leds_sent) < tmp_leds_per_update)
{
tmp_leds_per_update = leds_count - leds_sent;
}
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_LEDS;
usb_buf[0x02] = leds_sent;
usb_buf[0x03] = tmp_leds_per_update;
for (unsigned int led_idx = 0; led_idx < tmp_leds_per_update; led_idx++)
{
usb_buf[(led_idx * 3) + 4] = RGBGetRValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 3) + 5] = RGBGetGValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 3) + 6] = RGBGetBValue(colors[led_idx + leds_sent]);
}
hid_write(dev, usb_buf, 65);
if(delay > 0ms)
{
std::this_thread::sleep_for(delay);
}
leds_sent += tmp_leds_per_update;
}
}
@@ -1,32 +1,32 @@
/*---------------------------------------------------------*\
| QMKOpenRGBRevBController.h |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision B |
| |
| Kasper 28 Jun 2021 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "QMKOpenRGBBaseController.h"
class QMKOpenRGBRevBController : public QMKOpenRGBBaseController
{
public:
QMKOpenRGBRevBController(hid_device *dev_handle, const char *path);
~QMKOpenRGBRevBController();
//Virtual function implementations
void GetLEDInfo(unsigned int leds_count);
void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue);
void DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int num_colors);
//Protocol Specific functions
std::vector<unsigned int> GetEnabledModes();
private:
};
/*---------------------------------------------------------*\
| QMKOpenRGBRevBController.h |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision B |
| |
| Kasper 28 Jun 2021 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "QMKOpenRGBBaseController.h"
class QMKOpenRGBRevBController : public QMKOpenRGBBaseController
{
public:
QMKOpenRGBRevBController(hid_device *dev_handle, const char *path);
~QMKOpenRGBRevBController();
//Virtual function implementations
void GetLEDInfo(unsigned int leds_count);
void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue);
void DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int num_colors);
//Protocol Specific functions
std::vector<unsigned int> GetEnabledModes();
private:
};
@@ -1,101 +1,101 @@
/*---------------------------------------------------------*\
| RGBController_QMKOpenRGBRevB.h |
| |
| RGBController for OpenRGB QMK Keyboard Protocol |
| Revision B |
| |
| Kasper 28 Jun 2021 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include "RGBController.h"
#include "QMKOpenRGBRevBController.h"
#define NO_LED 0xFFFFFFFF
typedef std::vector<std::vector<unsigned int>> VectorMatrix;
class RGBController_QMKOpenRGBRevB : public RGBController
{
public:
RGBController_QMKOpenRGBRevB(QMKOpenRGBRevBController* controller_ptr, bool save);
~RGBController_QMKOpenRGBRevB();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
QMKOpenRGBRevBController* controller;
std::vector<unsigned int> flat_matrix_map;
std::vector<unsigned int> flat_underglow_map;
void InitializeMode
(
std::string name,
unsigned int &current_mode,
unsigned int flags,
unsigned int color_mode,
bool save
);
unsigned int CalculateDivisor
(
std::vector<point_t> led_points,
std::set<int> rows,
std::set<int> columns
);
void CountKeyTypes
(
std::vector<unsigned int> led_flags,
unsigned int total_led_count,
unsigned int& key_leds,
unsigned int& underglow_leds
);
void PlaceLEDsInMaps
(
std::set<int> unique_rows,
std::set<int> unique_cols,
unsigned int divisor,
std::vector<point_t> led_points,
std::vector<unsigned int> led_flags,
VectorMatrix& matrix_map_xl,
VectorMatrix& underglow_map_xl
);
VectorMatrix MakeEmptyMatrixMap
(
std::size_t height,
std::size_t width
);
void CleanMatrixMaps
(
VectorMatrix& matrix_map,
VectorMatrix& underglow_map,
unsigned int height,
bool has_underglow
);
std::vector<unsigned int> FlattenMatrixMap
(
VectorMatrix matrix_map
);
};
/*---------------------------------------------------------*\
| RGBController_QMKOpenRGBRevB.h |
| |
| RGBController for OpenRGB QMK Keyboard Protocol |
| Revision B |
| |
| Kasper 28 Jun 2021 |
| Jath03 28 May 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include "RGBController.h"
#include "QMKOpenRGBRevBController.h"
#define NO_LED 0xFFFFFFFF
typedef std::vector<std::vector<unsigned int>> VectorMatrix;
class RGBController_QMKOpenRGBRevB : public RGBController
{
public:
RGBController_QMKOpenRGBRevB(QMKOpenRGBRevBController* controller_ptr, bool save);
~RGBController_QMKOpenRGBRevB();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
QMKOpenRGBRevBController* controller;
std::vector<unsigned int> flat_matrix_map;
std::vector<unsigned int> flat_underglow_map;
void InitializeMode
(
std::string name,
unsigned int &current_mode,
unsigned int flags,
unsigned int color_mode,
bool save
);
unsigned int CalculateDivisor
(
std::vector<point_t> led_points,
std::set<int> rows,
std::set<int> columns
);
void CountKeyTypes
(
std::vector<unsigned int> led_flags,
unsigned int total_led_count,
unsigned int& key_leds,
unsigned int& underglow_leds
);
void PlaceLEDsInMaps
(
std::set<int> unique_rows,
std::set<int> unique_cols,
unsigned int divisor,
std::vector<point_t> led_points,
std::vector<unsigned int> led_flags,
VectorMatrix& matrix_map_xl,
VectorMatrix& underglow_map_xl
);
VectorMatrix MakeEmptyMatrixMap
(
std::size_t height,
std::size_t width
);
void CleanMatrixMaps
(
VectorMatrix& matrix_map,
VectorMatrix& underglow_map,
unsigned int height,
bool has_underglow
);
std::vector<unsigned int> FlattenMatrixMap
(
VectorMatrix matrix_map
);
};
@@ -1,225 +1,225 @@
/*---------------------------------------------------------*\
| QMKOpenRGBRevDController.cpp |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision D |
| |
| Neneya 26 Dec 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "QMKKeycodes.h"
#include "QMKOpenRGBRevDController.h"
using namespace std::chrono_literals;
QMKOpenRGBRevDController::QMKOpenRGBRevDController(hid_device *dev_handle, const char *path) :
QMKOpenRGBBaseController(dev_handle, path, 15)
{
}
QMKOpenRGBRevDController::~QMKOpenRGBRevDController()
{
}
std::vector<unsigned int> QMKOpenRGBRevDController::GetLEDValues()
{
return led_values;
}
void QMKOpenRGBRevDController::GetLEDInfo(unsigned int leds_count)
{
unsigned int leds_sent = 0;
unsigned int leds_per_update_info = 8;
std::vector<point_t> underglow_points;
std::vector<unsigned int> underglow_flags;
std::vector<std::string> underglow_names;
std::vector<RGBColor> underglow_colors;
std::vector<unsigned int> underglow_values;
while (leds_sent < leds_count)
{
if ((leds_count - leds_sent) < leds_per_update_info)
{
leds_per_update_info = leds_count - leds_sent;
}
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_LED_INFO;
usb_buf[0x02] = leds_sent;
usb_buf[0x03] = leds_per_update_info;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
for (unsigned int led_idx = 0; led_idx < leds_per_update_info; led_idx++)
{
unsigned int offset = led_idx * 7;
if(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE] != QMK_OPENRGB_FAILURE)
{
if(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE] & 2)
{
underglow_points.push_back(point_t{usb_buf[offset + QMK_OPENRGB_POINT_X_BYTE], usb_buf[offset + QMK_OPENRGB_POINT_Y_BYTE]});
underglow_flags.push_back(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE]);
underglow_colors.push_back(ToRGBColor(usb_buf[offset + QMK_OPENRGB_R_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_G_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_B_COLOR_BYTE]));
underglow_values.push_back((unsigned int)(underglow_values.size() + led_values.size()));
}
else
{
led_points.push_back(point_t{usb_buf[offset + QMK_OPENRGB_POINT_X_BYTE], usb_buf[offset + QMK_OPENRGB_POINT_Y_BYTE]});
led_flags.push_back(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE]);
led_colors.push_back(ToRGBColor(usb_buf[offset + QMK_OPENRGB_R_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_G_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_B_COLOR_BYTE]));
led_values.push_back((unsigned int)(underglow_values.size() + led_values.size()));
}
}
if(usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE] != 0)
{
if(qmk_keynames.count(usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE]) > 0)
{
led_names.push_back(qmk_keynames[usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE]]);
}
else
{
LOG_DEBUG("[%s] Key code: %d (%02X) @ offset %d was not found in the QMK keyname map",
device_name.c_str(), usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE],
usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE], leds_sent + led_idx);
led_names.push_back(KEY_EN_UNUSED);
}
}
else if(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE] & 2)
{
underglow_names.push_back("Underglow: " + std::to_string(underglow_names.size() + led_names.size()));
}
}
leds_sent += leds_per_update_info;
}
led_points.insert(led_points.end(), underglow_points.begin(), underglow_points.end());
led_flags.insert(led_flags.end(), underglow_flags.begin(), underglow_flags.end());
led_colors.insert(led_colors.end(), underglow_colors.begin(), underglow_colors.end());
led_names.insert(led_names.end(), underglow_names.begin(), underglow_names.end());
led_values.insert(led_values.end(), underglow_values.begin(), underglow_values.end());
}
std::vector<unsigned int> QMKOpenRGBRevDController::GetEnabledModes()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_ENABLED_MODES;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
std::vector<unsigned int> enabled_modes;
int i = 1;
while (usb_buf[i] != 0)
{
enabled_modes.push_back(usb_buf[i]);
i++;
}
return enabled_modes;
}
void QMKOpenRGBRevDController::DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED;
usb_buf[0x02] = led_values[led];
usb_buf[0x03] = red;
usb_buf[0x04] = green;
usb_buf[0x05] = blue;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT);
}
void QMKOpenRGBRevDController::DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int leds_count)
{
unsigned int leds_sent = 0;
unsigned int tmp_leds_per_update = leds_per_update;
while (leds_sent < leds_count)
{
if ((leds_count - leds_sent) < tmp_leds_per_update)
{
tmp_leds_per_update = leds_count - leds_sent;
}
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_LEDS;
usb_buf[0x02] = tmp_leds_per_update;
for (unsigned int led_idx = 0; led_idx < tmp_leds_per_update; led_idx++)
{
usb_buf[(led_idx * 4) + 3] = led_values[led_idx + leds_sent];
usb_buf[(led_idx * 4) + 4] = RGBGetRValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 4) + 5] = RGBGetGValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 4) + 6] = RGBGetBValue(colors[led_idx + leds_sent]);
}
hid_write(dev, usb_buf, 65);
if(delay > 0ms)
{
std::this_thread::sleep_for(delay);
}
leds_sent += tmp_leds_per_update;
}
}
/*---------------------------------------------------------*\
| QMKOpenRGBRevDController.cpp |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision D |
| |
| Neneya 26 Dec 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "QMKKeycodes.h"
#include "QMKOpenRGBRevDController.h"
using namespace std::chrono_literals;
QMKOpenRGBRevDController::QMKOpenRGBRevDController(hid_device *dev_handle, const char *path) :
QMKOpenRGBBaseController(dev_handle, path, 15)
{
}
QMKOpenRGBRevDController::~QMKOpenRGBRevDController()
{
}
std::vector<unsigned int> QMKOpenRGBRevDController::GetLEDValues()
{
return led_values;
}
void QMKOpenRGBRevDController::GetLEDInfo(unsigned int leds_count)
{
unsigned int leds_sent = 0;
unsigned int leds_per_update_info = 8;
std::vector<point_t> underglow_points;
std::vector<unsigned int> underglow_flags;
std::vector<std::string> underglow_names;
std::vector<RGBColor> underglow_colors;
std::vector<unsigned int> underglow_values;
while (leds_sent < leds_count)
{
if ((leds_count - leds_sent) < leds_per_update_info)
{
leds_per_update_info = leds_count - leds_sent;
}
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_LED_INFO;
usb_buf[0x02] = leds_sent;
usb_buf[0x03] = leds_per_update_info;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
for (unsigned int led_idx = 0; led_idx < leds_per_update_info; led_idx++)
{
unsigned int offset = led_idx * 7;
if(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE] != QMK_OPENRGB_FAILURE)
{
if(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE] & 2)
{
underglow_points.push_back(point_t{usb_buf[offset + QMK_OPENRGB_POINT_X_BYTE], usb_buf[offset + QMK_OPENRGB_POINT_Y_BYTE]});
underglow_flags.push_back(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE]);
underglow_colors.push_back(ToRGBColor(usb_buf[offset + QMK_OPENRGB_R_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_G_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_B_COLOR_BYTE]));
underglow_values.push_back((unsigned int)(underglow_values.size() + led_values.size()));
}
else
{
led_points.push_back(point_t{usb_buf[offset + QMK_OPENRGB_POINT_X_BYTE], usb_buf[offset + QMK_OPENRGB_POINT_Y_BYTE]});
led_flags.push_back(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE]);
led_colors.push_back(ToRGBColor(usb_buf[offset + QMK_OPENRGB_R_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_G_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_B_COLOR_BYTE]));
led_values.push_back((unsigned int)(underglow_values.size() + led_values.size()));
}
}
if(usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE] != 0)
{
if(qmk_keynames.count(usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE]) > 0)
{
led_names.push_back(qmk_keynames[usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE]]);
}
else
{
LOG_DEBUG("[%s] Key code: %d (%02X) @ offset %d was not found in the QMK keyname map",
device_name.c_str(), usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE],
usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE], leds_sent + led_idx);
led_names.push_back(KEY_EN_UNUSED);
}
}
else if(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE] & 2)
{
underglow_names.push_back("Underglow: " + std::to_string(underglow_names.size() + led_names.size()));
}
}
leds_sent += leds_per_update_info;
}
led_points.insert(led_points.end(), underglow_points.begin(), underglow_points.end());
led_flags.insert(led_flags.end(), underglow_flags.begin(), underglow_flags.end());
led_colors.insert(led_colors.end(), underglow_colors.begin(), underglow_colors.end());
led_names.insert(led_names.end(), underglow_names.begin(), underglow_names.end());
led_values.insert(led_values.end(), underglow_values.begin(), underglow_values.end());
}
std::vector<unsigned int> QMKOpenRGBRevDController::GetEnabledModes()
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_GET_ENABLED_MODES;
int bytes_read = 0;
do
{
hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE);
bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT);
} while(bytes_read <= 0);
std::vector<unsigned int> enabled_modes;
int i = 1;
while (usb_buf[i] != 0)
{
enabled_modes.push_back(usb_buf[i]);
i++;
}
return enabled_modes;
}
void QMKOpenRGBRevDController::DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue)
{
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED;
usb_buf[0x02] = led_values[led];
usb_buf[0x03] = red;
usb_buf[0x04] = green;
usb_buf[0x05] = blue;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT);
}
void QMKOpenRGBRevDController::DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int leds_count)
{
unsigned int leds_sent = 0;
unsigned int tmp_leds_per_update = leds_per_update;
while (leds_sent < leds_count)
{
if ((leds_count - leds_sent) < tmp_leds_per_update)
{
tmp_leds_per_update = leds_count - leds_sent;
}
unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE);
/*-----------------------------------------------------*\
| Set up config table request packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x00;
usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_LEDS;
usb_buf[0x02] = tmp_leds_per_update;
for (unsigned int led_idx = 0; led_idx < tmp_leds_per_update; led_idx++)
{
usb_buf[(led_idx * 4) + 3] = led_values[led_idx + leds_sent];
usb_buf[(led_idx * 4) + 4] = RGBGetRValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 4) + 5] = RGBGetGValue(colors[led_idx + leds_sent]);
usb_buf[(led_idx * 4) + 6] = RGBGetBValue(colors[led_idx + leds_sent]);
}
hid_write(dev, usb_buf, 65);
if(delay > 0ms)
{
std::this_thread::sleep_for(delay);
}
leds_sent += tmp_leds_per_update;
}
}
@@ -1,33 +1,33 @@
/*---------------------------------------------------------*\
| QMKOpenRGBRevDController.h |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision D |
| |
| Neneya 26 Dec 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "QMKOpenRGBBaseController.h"
class QMKOpenRGBRevDController : public QMKOpenRGBBaseController
{
public:
QMKOpenRGBRevDController(hid_device *dev_handle, const char *path);
~QMKOpenRGBRevDController();
//Virtual function implementations
void GetLEDInfo(unsigned int leds_count);
void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue);
void DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int num_colors);
//Protocol Specific functions
std::vector<unsigned int> GetLEDValues();
std::vector<unsigned int> GetEnabledModes();
private:
std::vector<unsigned int> led_values;
};
/*---------------------------------------------------------*\
| QMKOpenRGBRevDController.h |
| |
| Driver for OpenRGB QMK Keyboard Protocol Revision D |
| |
| Neneya 26 Dec 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "QMKOpenRGBBaseController.h"
class QMKOpenRGBRevDController : public QMKOpenRGBBaseController
{
public:
QMKOpenRGBRevDController(hid_device *dev_handle, const char *path);
~QMKOpenRGBRevDController();
//Virtual function implementations
void GetLEDInfo(unsigned int leds_count);
void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue);
void DirectModeSetLEDs(std::vector<RGBColor> colors, unsigned int num_colors);
//Protocol Specific functions
std::vector<unsigned int> GetLEDValues();
std::vector<unsigned int> GetEnabledModes();
private:
std::vector<unsigned int> led_values;
};
@@ -1,100 +1,100 @@
/*---------------------------------------------------------*\
| RGBController_QMKOpenRGBRevD.h |
| |
| RGBController for OpenRGB QMK Keyboard Protocol |
| Revision D |
| |
| Neneya 26 Dec 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include "RGBController.h"
#include "QMKOpenRGBRevDController.h"
#define NO_LED 0xFFFFFFFF
typedef std::vector<std::vector<unsigned int>> VectorMatrix;
class RGBController_QMKOpenRGBRevD : public RGBController
{
public:
RGBController_QMKOpenRGBRevD(QMKOpenRGBRevDController* controller_ptr, bool save);
~RGBController_QMKOpenRGBRevD();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
QMKOpenRGBRevDController* controller;
std::vector<unsigned int> flat_matrix_map;
std::vector<unsigned int> flat_underglow_map;
void InitializeMode
(
std::string name,
unsigned int &current_mode,
unsigned int flags,
unsigned int color_mode,
bool save
);
unsigned int CalculateDivisor
(
std::vector<point_t> led_points,
std::set<int> rows,
std::set<int> columns
);
void CountKeyTypes
(
std::vector<unsigned int> led_flags,
unsigned int total_led_count,
unsigned int& key_leds,
unsigned int& underglow_leds
);
void PlaceLEDsInMaps
(
std::set<int> unique_rows,
std::set<int> unique_cols,
unsigned int divisor,
std::vector<point_t> led_points,
std::vector<unsigned int> led_flags,
VectorMatrix& matrix_map_xl,
VectorMatrix& underglow_map_xl
);
VectorMatrix MakeEmptyMatrixMap
(
std::size_t height,
std::size_t width
);
void CleanMatrixMaps
(
VectorMatrix& matrix_map,
VectorMatrix& underglow_map,
unsigned int height,
bool has_underglow
);
std::vector<unsigned int> FlattenMatrixMap
(
VectorMatrix matrix_map
);
};
/*---------------------------------------------------------*\
| RGBController_QMKOpenRGBRevD.h |
| |
| RGBController for OpenRGB QMK Keyboard Protocol |
| Revision D |
| |
| Neneya 26 Dec 2021 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include "RGBController.h"
#include "QMKOpenRGBRevDController.h"
#define NO_LED 0xFFFFFFFF
typedef std::vector<std::vector<unsigned int>> VectorMatrix;
class RGBController_QMKOpenRGBRevD : public RGBController
{
public:
RGBController_QMKOpenRGBRevD(QMKOpenRGBRevDController* controller_ptr, bool save);
~RGBController_QMKOpenRGBRevD();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
QMKOpenRGBRevDController* controller;
std::vector<unsigned int> flat_matrix_map;
std::vector<unsigned int> flat_underglow_map;
void InitializeMode
(
std::string name,
unsigned int &current_mode,
unsigned int flags,
unsigned int color_mode,
bool save
);
unsigned int CalculateDivisor
(
std::vector<point_t> led_points,
std::set<int> rows,
std::set<int> columns
);
void CountKeyTypes
(
std::vector<unsigned int> led_flags,
unsigned int total_led_count,
unsigned int& key_leds,
unsigned int& underglow_leds
);
void PlaceLEDsInMaps
(
std::set<int> unique_rows,
std::set<int> unique_cols,
unsigned int divisor,
std::vector<point_t> led_points,
std::vector<unsigned int> led_flags,
VectorMatrix& matrix_map_xl,
VectorMatrix& underglow_map_xl
);
VectorMatrix MakeEmptyMatrixMap
(
std::size_t height,
std::size_t width
);
void CleanMatrixMaps
(
VectorMatrix& matrix_map,
VectorMatrix& underglow_map,
unsigned int height,
bool has_underglow
);
std::vector<unsigned int> FlattenMatrixMap
(
VectorMatrix matrix_map
);
};
@@ -1,101 +1,101 @@
/*---------------------------------------------------------*\
| RGBController_QMKOpenRGBRevE.h |
| |
| RGBController for OpenRGB QMK Keyboard Protocol |
| Revision E |
| |
| Neneya 26 Dec 2021 |
| HorrorTroll 11 Jul 2022 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include "RGBController.h"
#include "QMKOpenRGBRevDController.h"
#define NO_LED 0xFFFFFFFF
typedef std::vector<std::vector<unsigned int>> VectorMatrix;
class RGBController_QMKOpenRGBRevE : public RGBController
{
public:
RGBController_QMKOpenRGBRevE(QMKOpenRGBRevDController* controller_ptr, bool save);
~RGBController_QMKOpenRGBRevE();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
QMKOpenRGBRevDController* controller;
std::vector<unsigned int> flat_matrix_map;
std::vector<unsigned int> flat_underglow_map;
void InitializeMode
(
std::string name,
unsigned int &current_mode,
unsigned int flags,
unsigned int color_mode,
bool save
);
unsigned int CalculateDivisor
(
std::vector<point_t> led_points,
std::set<int> rows,
std::set<int> columns
);
void CountKeyTypes
(
std::vector<unsigned int> led_flags,
unsigned int total_led_count,
unsigned int& key_leds,
unsigned int& underglow_leds
);
void PlaceLEDsInMaps
(
std::set<int> unique_rows,
std::set<int> unique_cols,
unsigned int divisor,
std::vector<point_t> led_points,
std::vector<unsigned int> led_flags,
VectorMatrix& matrix_map_xl,
VectorMatrix& underglow_map_xl
);
VectorMatrix MakeEmptyMatrixMap
(
std::size_t height,
std::size_t width
);
void CleanMatrixMaps
(
VectorMatrix& matrix_map,
VectorMatrix& underglow_map,
unsigned int height,
bool has_underglow
);
std::vector<unsigned int> FlattenMatrixMap
(
VectorMatrix matrix_map
);
};
/*---------------------------------------------------------*\
| RGBController_QMKOpenRGBRevE.h |
| |
| RGBController for OpenRGB QMK Keyboard Protocol |
| Revision E |
| |
| Neneya 26 Dec 2021 |
| HorrorTroll 11 Jul 2022 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include "RGBController.h"
#include "QMKOpenRGBRevDController.h"
#define NO_LED 0xFFFFFFFF
typedef std::vector<std::vector<unsigned int>> VectorMatrix;
class RGBController_QMKOpenRGBRevE : public RGBController
{
public:
RGBController_QMKOpenRGBRevE(QMKOpenRGBRevDController* controller_ptr, bool save);
~RGBController_QMKOpenRGBRevE();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
QMKOpenRGBRevDController* controller;
std::vector<unsigned int> flat_matrix_map;
std::vector<unsigned int> flat_underglow_map;
void InitializeMode
(
std::string name,
unsigned int &current_mode,
unsigned int flags,
unsigned int color_mode,
bool save
);
unsigned int CalculateDivisor
(
std::vector<point_t> led_points,
std::set<int> rows,
std::set<int> columns
);
void CountKeyTypes
(
std::vector<unsigned int> led_flags,
unsigned int total_led_count,
unsigned int& key_leds,
unsigned int& underglow_leds
);
void PlaceLEDsInMaps
(
std::set<int> unique_rows,
std::set<int> unique_cols,
unsigned int divisor,
std::vector<point_t> led_points,
std::vector<unsigned int> led_flags,
VectorMatrix& matrix_map_xl,
VectorMatrix& underglow_map_xl
);
VectorMatrix MakeEmptyMatrixMap
(
std::size_t height,
std::size_t width
);
void CleanMatrixMaps
(
VectorMatrix& matrix_map,
VectorMatrix& underglow_map,
unsigned int height,
bool has_underglow
);
std::vector<unsigned int> FlattenMatrixMap
(
VectorMatrix matrix_map
);
};
+71 -71
View File
@@ -1,71 +1,71 @@
/*---------------------------------------------------------*\
| QMKViaCommands.h |
| |
| List of QMK VIA command values |
| |
| Adam Honse <calcprogrammer1@gmail.com) 21 Jun 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
enum
{
QMK_VIA_CMD_GET_PROTOCOL_VERSION = 0x01,
QMK_VIA_CMD_GET_KEYBOARD_VALUE = 0x02,
QMK_VIA_CMD_SET_KEYBOARD_VALUE = 0x03,
QMK_VIA_CMD_VIA_DYNAMIC_KEYMAP_GET_KEYCODE = 0x04,
QMK_VIA_CMD_VIA_DYNAMIC_KEYMAP_SET_KEYCODE = 0x05,
QMK_VIA_CMD_VIA_DYNAMIC_KEYMAP_RESET = 0x06,
QMK_VIA_CMD_CUSTOM_SET_VALUE = 0x07,
QMK_VIA_CMD_CUSTOM_GET_VALUE = 0x08,
QMK_VIA_CMD_CUSTOM_SAVE = 0x09,
QMK_VIA_CMD_EEPROM_RESET = 0x0A,
};
enum
{
QMK_VIA_CUSTOM_CHANNEL = 0,
QMK_VIA_BACKLIGHT_CHANNEL = 1,
QMK_VIA_RGBLIGHT_CHANNEL = 2,
QMK_VIA_RGB_MATRIX_CHANNEL = 3,
QMK_VIA_AUDIO_CHANNEL = 4,
QMK_VIA_LED_MATRIX_CHANNEL = 5,
};
enum
{
QMK_VIA_BACKLIGHT_BRIGHTNESS = 1,
QMK_VIA_BACKLIGHT_EFFECT = 2,
};
enum
{
QMK_VIA_RGBLIGHT_BRIGHTNESS = 1,
QMK_VIA_RGBLIGHT_EFFECT = 2,
QMK_VIA_RGBLIGHT_EFFECT_SPEED = 3,
QMK_VIA_RGBLIGHT_COLOR = 4,
};
enum
{
QMK_VIA_RGB_MATRIX_BRIGHTNESS = 1,
QMK_VIA_RGB_MATRIX_EFFECT = 2,
QMK_VIA_RGB_MATRIX_EFFECT_SPEED = 3,
QMK_VIA_RGB_MATRIX_COLOR = 4,
};
enum
{
QMK_VIA_LED_MATRIX_BRIGHTNESS = 1,
QMK_VIA_LED_MATRIX_EFFECT = 2,
QMK_VIA_LED_MATRIX_EFFECT_SPEED = 3,
};
enum
{
QMK_VIA_AUDIO_ENABLE = 1,
QMK_VIA_AUDIO_CLICKY_ENABLE = 2,
};
/*---------------------------------------------------------*\
| QMKViaCommands.h |
| |
| List of QMK VIA command values |
| |
| Adam Honse <calcprogrammer1@gmail.com) 21 Jun 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
enum
{
QMK_VIA_CMD_GET_PROTOCOL_VERSION = 0x01,
QMK_VIA_CMD_GET_KEYBOARD_VALUE = 0x02,
QMK_VIA_CMD_SET_KEYBOARD_VALUE = 0x03,
QMK_VIA_CMD_VIA_DYNAMIC_KEYMAP_GET_KEYCODE = 0x04,
QMK_VIA_CMD_VIA_DYNAMIC_KEYMAP_SET_KEYCODE = 0x05,
QMK_VIA_CMD_VIA_DYNAMIC_KEYMAP_RESET = 0x06,
QMK_VIA_CMD_CUSTOM_SET_VALUE = 0x07,
QMK_VIA_CMD_CUSTOM_GET_VALUE = 0x08,
QMK_VIA_CMD_CUSTOM_SAVE = 0x09,
QMK_VIA_CMD_EEPROM_RESET = 0x0A,
};
enum
{
QMK_VIA_CUSTOM_CHANNEL = 0,
QMK_VIA_BACKLIGHT_CHANNEL = 1,
QMK_VIA_RGBLIGHT_CHANNEL = 2,
QMK_VIA_RGB_MATRIX_CHANNEL = 3,
QMK_VIA_AUDIO_CHANNEL = 4,
QMK_VIA_LED_MATRIX_CHANNEL = 5,
};
enum
{
QMK_VIA_BACKLIGHT_BRIGHTNESS = 1,
QMK_VIA_BACKLIGHT_EFFECT = 2,
};
enum
{
QMK_VIA_RGBLIGHT_BRIGHTNESS = 1,
QMK_VIA_RGBLIGHT_EFFECT = 2,
QMK_VIA_RGBLIGHT_EFFECT_SPEED = 3,
QMK_VIA_RGBLIGHT_COLOR = 4,
};
enum
{
QMK_VIA_RGB_MATRIX_BRIGHTNESS = 1,
QMK_VIA_RGB_MATRIX_EFFECT = 2,
QMK_VIA_RGB_MATRIX_EFFECT_SPEED = 3,
QMK_VIA_RGB_MATRIX_COLOR = 4,
};
enum
{
QMK_VIA_LED_MATRIX_BRIGHTNESS = 1,
QMK_VIA_LED_MATRIX_EFFECT = 2,
QMK_VIA_LED_MATRIX_EFFECT_SPEED = 3,
};
enum
{
QMK_VIA_AUDIO_ENABLE = 1,
QMK_VIA_AUDIO_CLICKY_ENABLE = 2,
};
@@ -1,473 +1,473 @@
/*---------------------------------------------------------*\
| QMKVialRGBController.cpp |
| |
| Driver for VialRGB QMK Keyboard Protocol |
| |
| Adam Honse <calcprogrammer1@gmail.com) 29 Sep 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <stdio.h>
#include "hsv.h"
#include "QMKVialRGBController.h"
#include "StringUtils.h"
/*---------------------------------------------------------*\
| Portions of this controller adapted from Raspberry Pi |
| RPiKeyboardConfig utility: |
| https://github.com/raspberrypi/rpi-keyboard-config |
\*---------------------------------------------------------*/
QMKVialRGBController::QMKVialRGBController(hid_device *dev_handle, const char *path)
{
/*-----------------------------------------------------*\
| Initialize controller fields |
\*-----------------------------------------------------*/
dev = dev_handle;
location = path;
supported = false;
/*-----------------------------------------------------*\
| Read product string |
\*-----------------------------------------------------*/
wchar_t product_string[256];
int ret = hid_get_product_string(dev, product_string, 256);
if(ret != 0)
{
name = "";
}
else
{
name = StringUtils::wstring_to_string(product_string);
}
/*-----------------------------------------------------*\
| Read vendor string |
\*-----------------------------------------------------*/
wchar_t vendor_string[256];
ret = hid_get_manufacturer_string(dev, vendor_string, 256);
if(ret != 0)
{
vendor = "";
}
else
{
vendor = StringUtils::wstring_to_string(vendor_string);
}
/*-----------------------------------------------------*\
| Read serial string |
\*-----------------------------------------------------*/
wchar_t serial_string[256];
ret = hid_get_serial_number_string(dev, serial_string, 256);
if(ret != 0)
{
serial = "";
}
else
{
serial = StringUtils::wstring_to_string(serial_string);
}
/*-----------------------------------------------------*\
| Get VIA, Vial, and VialRGB information |
\*-----------------------------------------------------*/
CmdGetViaProtocolVersion(&via_protocol_version);
if(via_protocol_version < 9)
{
return;
}
CmdGetVialInfo(&vial_protocol_version, &keyboard_uid, &vialrgb_flag);
if((vial_protocol_version < 4) || ((vialrgb_flag & 1) == 0))
{
supported = false;
return;
}
CmdGetVialRGBInfo(&vialrgb_protocol_version, &maximum_brightness);
/*-----------------------------------------------------*\
| Get list of supported effects |
\*-----------------------------------------------------*/
CmdGetSupportedEffects();
/*-----------------------------------------------------*\
| Get count of LEDs |
\*-----------------------------------------------------*/
CmdGetNumberLEDs(&number_leds);
/*-----------------------------------------------------*\
| Get info and keycode for all LEDs |
\*-----------------------------------------------------*/
for(unsigned short led_index = 0; led_index < number_leds; led_index++)
{
led_info.push_back(CmdGetLEDInfo(led_index));
keycodes.push_back(CmdGetKeycode(0, led_info[led_index].row, led_info[led_index].col));
}
supported = true;
}
QMKVialRGBController::~QMKVialRGBController()
{
hid_close(dev);
}
std::string QMKVialRGBController::GetLocation()
{
return("HID: " + location);
}
std::string QMKVialRGBController::GetName()
{
return(name);
}
std::string QMKVialRGBController::GetSerial()
{
return(serial);
}
std::string QMKVialRGBController::GetVendor()
{
return(vendor);
}
std::string QMKVialRGBController::GetVersion()
{
/*-----------------------------------------------------*\
| Format UID string |
\*-----------------------------------------------------*/
char uid_buf[17];
snprintf(uid_buf, sizeof(uid_buf), "%016llX", keyboard_uid);
/*-----------------------------------------------------*\
| Format multi-line version text |
\*-----------------------------------------------------*/
return("VIA: " + std::to_string(via_protocol_version) + "\r\n"
+ "Vial: " + std::to_string(vial_protocol_version) + "\r\n"
+ "VialRGB: " + std::to_string(vialrgb_protocol_version) + "\r\n"
+ "UID: " + uid_buf);
}
bool QMKVialRGBController::GetSupported()
{
return(supported);
}
unsigned short QMKVialRGBController::GetEffect(std::size_t effect_idx)
{
return(supported_effects[effect_idx]);
}
std::size_t QMKVialRGBController::GetEffectCount()
{
return(supported_effects.size());
}
unsigned short QMKVialRGBController::GetKeycode(unsigned short led_index)
{
return(keycodes[led_index]);
}
unsigned short QMKVialRGBController::GetLEDCount()
{
return(number_leds);
}
qmk_rgb_matrix_led_info QMKVialRGBController::GetLEDInfo(unsigned short led_index)
{
return(led_info[led_index]);
}
void QMKVialRGBController::GetMode
(
unsigned short* mode,
unsigned char* speed,
unsigned char* hue,
unsigned char* sat,
unsigned char* val
)
{
CmdGetMode(mode, speed, hue, sat, val);
}
void QMKVialRGBController::SendLEDs
(
unsigned short number_leds,
RGBColor* color_data
)
{
unsigned short led_start_index = 0;
unsigned char number_packet_leds = 9;
while(led_start_index < number_leds)
{
if((number_leds - led_start_index) < 9)
{
number_packet_leds = (number_leds - led_start_index);
}
CmdSendLEDs(led_start_index, number_packet_leds, &color_data[led_start_index]);
led_start_index += number_packet_leds;
}
}
void QMKVialRGBController::SetMode
(
unsigned short mode,
unsigned char speed,
unsigned char hue,
unsigned char sat,
unsigned char val
)
{
CmdSetMode(mode, speed, hue, sat, val);
}
unsigned short QMKVialRGBController::CmdGetKeycode
(
unsigned char layer,
unsigned char row,
unsigned char col
)
{
unsigned char data[5];
unsigned short keycode;
data[0] = row;
data[1] = col;
SendCheckCommand(CMD_VIA_DYNAMIC_KEYMAP_GET_KEYCODE, layer, data, 2, data, 5);
memcpy(&keycode, &data[3], sizeof(unsigned short));
return(keycode);
}
qmk_rgb_matrix_led_info QMKVialRGBController::CmdGetLEDInfo
(
unsigned short led_index
)
{
qmk_rgb_matrix_led_info data;
SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_LED_INFO, (unsigned char*)&led_index, sizeof(led_index), (unsigned char*)&data, sizeof(data));
return(data);
}
void QMKVialRGBController::CmdGetMode
(
unsigned short* mode,
unsigned char* speed,
unsigned char* hue,
unsigned char* sat,
unsigned char* val
)
{
unsigned char data[6];
SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_MODE, NULL, 0, data, 6);
memcpy(mode, &data[0], sizeof(unsigned short));
*speed = data[2];
*hue = data[3];
*sat = data[4];
*val = data[5];
}
void QMKVialRGBController::CmdGetNumberLEDs
(
unsigned short* number_leds
)
{
SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_NUMBER_LEDS, NULL, 0, (unsigned char*)number_leds, sizeof(unsigned short));
}
void QMKVialRGBController::CmdGetSupportedEffects()
{
unsigned short packet_effects[15];
unsigned short max_effect = 0;
supported_effects.clear();
supported_effects.push_back(0);
while(max_effect < VIALRGB_EFFECT_SKIP)
{
SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_SUPPORTED, (unsigned char*)&max_effect, sizeof(max_effect), (unsigned char*)packet_effects, sizeof(packet_effects));
for(unsigned int effect_idx = 0; effect_idx < 15; effect_idx++)
{
if(packet_effects[effect_idx] == VIALRGB_EFFECT_SKIP)
{
return;
}
supported_effects.push_back(packet_effects[effect_idx]);
max_effect = packet_effects[effect_idx];
}
}
}
void QMKVialRGBController::CmdGetVialInfo
(
unsigned int* vial_protocol,
unsigned long long* keyboard_uid,
unsigned char* vialrgb_flag
)
{
unsigned char data[sizeof(int) + sizeof(unsigned long long) + sizeof(unsigned char)];
SendCommand(CMD_VIAL_COMMAND, VIAL_GET_KEYBOARD_ID, NULL, 0, data, sizeof(data));
memcpy(vial_protocol, &data[0], sizeof(int));
memcpy(keyboard_uid, &data[sizeof(int)], sizeof(unsigned long long));
memcpy(vialrgb_flag, &data[sizeof(int) + sizeof(unsigned long long)], sizeof(unsigned char));
}
void QMKVialRGBController::CmdGetVialRGBInfo
(
unsigned short* vialrgb_protocol_version,
unsigned char* maximum_brightness
)
{
unsigned char data[sizeof(unsigned short) + sizeof(unsigned char)];
SendCommand(CMD_VIAL_COMMAND, VIAL_GET_KEYBOARD_ID, NULL, 0, data, sizeof(data));
memcpy(vialrgb_protocol_version, &data[0], sizeof(unsigned short));
memcpy(maximum_brightness, &data[sizeof(unsigned char)], sizeof(unsigned short));
}
void QMKVialRGBController::CmdGetViaProtocolVersion
(
unsigned short* via_protocol_version
)
{
SendCheckCommand(CMD_GET_PROTOCOL_VERSION, 0, NULL, 0, (unsigned char*)via_protocol_version, sizeof(unsigned int));
}
void QMKVialRGBController::CmdSendLEDs
(
unsigned short start_index,
unsigned char number_leds,
RGBColor* color_data
)
{
unsigned char data[30];
memcpy(&data[0], &start_index, sizeof(start_index));
memcpy(&data[2], &number_leds, sizeof(number_leds));
if(number_leds > 9)
{
number_leds = 9;
}
for(unsigned char led_index = 0; led_index < number_leds; led_index++)
{
/*-------------------------------------------------*\
| VialRGB sends direct packets in HSV for some |
| inexplicable reason, so do the RGB to HSV |
| conversion before sending |
\*-------------------------------------------------*/
hsv_t hsv_color;
rgb2hsv(color_data[led_index], &hsv_color);
data[3 + (led_index * 3)] = (unsigned char)((float)hsv_color.hue * (256.0f / 360.0f));
data[4 + (led_index * 3)] = hsv_color.saturation;
data[5 + (led_index * 3)] = hsv_color.value;
}
SendCheckCommand(CMD_LIGHTING_SET_VALUE, VIALRGB_DIRECT_FASTSET, data, sizeof(data), NULL, 0);
}
void QMKVialRGBController::CmdSetMode
(
unsigned short mode,
unsigned char speed,
unsigned char hue,
unsigned char sat,
unsigned char val
)
{
unsigned char data[6];
memcpy(&data[0], &mode, sizeof(unsigned short));
data[2] = speed;
data[3] = hue;
data[4] = sat;
data[5] = val;
SendCommand(CMD_LIGHTING_SET_VALUE, VIALRGB_SET_MODE, data, sizeof(data), NULL, 0);
}
int QMKVialRGBController::SendCommand
(
unsigned char cmd,
unsigned char subcmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
)
{
unsigned char usb_buf[MSG_LEN + 1];
memset(usb_buf, 0, sizeof(usb_buf));
usb_buf[0] = 0x00;
usb_buf[1] = cmd;
usb_buf[2] = subcmd;
memcpy(&usb_buf[3], data_in, data_in_size);
hid_write(dev, usb_buf, sizeof(usb_buf));
int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf)-1, 1000);
memcpy(data_out, &usb_buf[0], data_out_size);
return(bytes_received);
}
int QMKVialRGBController::SendCheckCommand
(
unsigned char cmd,
unsigned char subcmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
)
{
unsigned char usb_buf[MSG_LEN + 1];
memset(usb_buf, 0, sizeof(usb_buf));
usb_buf[0] = 0x00;
usb_buf[1] = cmd;
usb_buf[2] = subcmd;
memcpy(&usb_buf[3], data_in, data_in_size);
hid_write(dev, usb_buf, sizeof(usb_buf));
int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf)-1, 1000);
memcpy(data_out, &usb_buf[2], data_out_size);
return(bytes_received);
}
/*---------------------------------------------------------*\
| QMKVialRGBController.cpp |
| |
| Driver for VialRGB QMK Keyboard Protocol |
| |
| Adam Honse <calcprogrammer1@gmail.com) 29 Sep 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <stdio.h>
#include "hsv.h"
#include "QMKVialRGBController.h"
#include "StringUtils.h"
/*---------------------------------------------------------*\
| Portions of this controller adapted from Raspberry Pi |
| RPiKeyboardConfig utility: |
| https://github.com/raspberrypi/rpi-keyboard-config |
\*---------------------------------------------------------*/
QMKVialRGBController::QMKVialRGBController(hid_device *dev_handle, const char *path)
{
/*-----------------------------------------------------*\
| Initialize controller fields |
\*-----------------------------------------------------*/
dev = dev_handle;
location = path;
supported = false;
/*-----------------------------------------------------*\
| Read product string |
\*-----------------------------------------------------*/
wchar_t product_string[256];
int ret = hid_get_product_string(dev, product_string, 256);
if(ret != 0)
{
name = "";
}
else
{
name = StringUtils::wstring_to_string(product_string);
}
/*-----------------------------------------------------*\
| Read vendor string |
\*-----------------------------------------------------*/
wchar_t vendor_string[256];
ret = hid_get_manufacturer_string(dev, vendor_string, 256);
if(ret != 0)
{
vendor = "";
}
else
{
vendor = StringUtils::wstring_to_string(vendor_string);
}
/*-----------------------------------------------------*\
| Read serial string |
\*-----------------------------------------------------*/
wchar_t serial_string[256];
ret = hid_get_serial_number_string(dev, serial_string, 256);
if(ret != 0)
{
serial = "";
}
else
{
serial = StringUtils::wstring_to_string(serial_string);
}
/*-----------------------------------------------------*\
| Get VIA, Vial, and VialRGB information |
\*-----------------------------------------------------*/
CmdGetViaProtocolVersion(&via_protocol_version);
if(via_protocol_version < 9)
{
return;
}
CmdGetVialInfo(&vial_protocol_version, &keyboard_uid, &vialrgb_flag);
if((vial_protocol_version < 4) || ((vialrgb_flag & 1) == 0))
{
supported = false;
return;
}
CmdGetVialRGBInfo(&vialrgb_protocol_version, &maximum_brightness);
/*-----------------------------------------------------*\
| Get list of supported effects |
\*-----------------------------------------------------*/
CmdGetSupportedEffects();
/*-----------------------------------------------------*\
| Get count of LEDs |
\*-----------------------------------------------------*/
CmdGetNumberLEDs(&number_leds);
/*-----------------------------------------------------*\
| Get info and keycode for all LEDs |
\*-----------------------------------------------------*/
for(unsigned short led_index = 0; led_index < number_leds; led_index++)
{
led_info.push_back(CmdGetLEDInfo(led_index));
keycodes.push_back(CmdGetKeycode(0, led_info[led_index].row, led_info[led_index].col));
}
supported = true;
}
QMKVialRGBController::~QMKVialRGBController()
{
hid_close(dev);
}
std::string QMKVialRGBController::GetLocation()
{
return("HID: " + location);
}
std::string QMKVialRGBController::GetName()
{
return(name);
}
std::string QMKVialRGBController::GetSerial()
{
return(serial);
}
std::string QMKVialRGBController::GetVendor()
{
return(vendor);
}
std::string QMKVialRGBController::GetVersion()
{
/*-----------------------------------------------------*\
| Format UID string |
\*-----------------------------------------------------*/
char uid_buf[17];
snprintf(uid_buf, sizeof(uid_buf), "%016llX", keyboard_uid);
/*-----------------------------------------------------*\
| Format multi-line version text |
\*-----------------------------------------------------*/
return("VIA: " + std::to_string(via_protocol_version) + "\r\n"
+ "Vial: " + std::to_string(vial_protocol_version) + "\r\n"
+ "VialRGB: " + std::to_string(vialrgb_protocol_version) + "\r\n"
+ "UID: " + uid_buf);
}
bool QMKVialRGBController::GetSupported()
{
return(supported);
}
unsigned short QMKVialRGBController::GetEffect(std::size_t effect_idx)
{
return(supported_effects[effect_idx]);
}
std::size_t QMKVialRGBController::GetEffectCount()
{
return(supported_effects.size());
}
unsigned short QMKVialRGBController::GetKeycode(unsigned short led_index)
{
return(keycodes[led_index]);
}
unsigned short QMKVialRGBController::GetLEDCount()
{
return(number_leds);
}
qmk_rgb_matrix_led_info QMKVialRGBController::GetLEDInfo(unsigned short led_index)
{
return(led_info[led_index]);
}
void QMKVialRGBController::GetMode
(
unsigned short* mode,
unsigned char* speed,
unsigned char* hue,
unsigned char* sat,
unsigned char* val
)
{
CmdGetMode(mode, speed, hue, sat, val);
}
void QMKVialRGBController::SendLEDs
(
unsigned short number_leds,
RGBColor* color_data
)
{
unsigned short led_start_index = 0;
unsigned char number_packet_leds = 9;
while(led_start_index < number_leds)
{
if((number_leds - led_start_index) < 9)
{
number_packet_leds = (number_leds - led_start_index);
}
CmdSendLEDs(led_start_index, number_packet_leds, &color_data[led_start_index]);
led_start_index += number_packet_leds;
}
}
void QMKVialRGBController::SetMode
(
unsigned short mode,
unsigned char speed,
unsigned char hue,
unsigned char sat,
unsigned char val
)
{
CmdSetMode(mode, speed, hue, sat, val);
}
unsigned short QMKVialRGBController::CmdGetKeycode
(
unsigned char layer,
unsigned char row,
unsigned char col
)
{
unsigned char data[5];
unsigned short keycode;
data[0] = row;
data[1] = col;
SendCheckCommand(CMD_VIA_DYNAMIC_KEYMAP_GET_KEYCODE, layer, data, 2, data, 5);
memcpy(&keycode, &data[3], sizeof(unsigned short));
return(keycode);
}
qmk_rgb_matrix_led_info QMKVialRGBController::CmdGetLEDInfo
(
unsigned short led_index
)
{
qmk_rgb_matrix_led_info data;
SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_LED_INFO, (unsigned char*)&led_index, sizeof(led_index), (unsigned char*)&data, sizeof(data));
return(data);
}
void QMKVialRGBController::CmdGetMode
(
unsigned short* mode,
unsigned char* speed,
unsigned char* hue,
unsigned char* sat,
unsigned char* val
)
{
unsigned char data[6];
SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_MODE, NULL, 0, data, 6);
memcpy(mode, &data[0], sizeof(unsigned short));
*speed = data[2];
*hue = data[3];
*sat = data[4];
*val = data[5];
}
void QMKVialRGBController::CmdGetNumberLEDs
(
unsigned short* number_leds
)
{
SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_NUMBER_LEDS, NULL, 0, (unsigned char*)number_leds, sizeof(unsigned short));
}
void QMKVialRGBController::CmdGetSupportedEffects()
{
unsigned short packet_effects[15];
unsigned short max_effect = 0;
supported_effects.clear();
supported_effects.push_back(0);
while(max_effect < VIALRGB_EFFECT_SKIP)
{
SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_SUPPORTED, (unsigned char*)&max_effect, sizeof(max_effect), (unsigned char*)packet_effects, sizeof(packet_effects));
for(unsigned int effect_idx = 0; effect_idx < 15; effect_idx++)
{
if(packet_effects[effect_idx] == VIALRGB_EFFECT_SKIP)
{
return;
}
supported_effects.push_back(packet_effects[effect_idx]);
max_effect = packet_effects[effect_idx];
}
}
}
void QMKVialRGBController::CmdGetVialInfo
(
unsigned int* vial_protocol,
unsigned long long* keyboard_uid,
unsigned char* vialrgb_flag
)
{
unsigned char data[sizeof(int) + sizeof(unsigned long long) + sizeof(unsigned char)];
SendCommand(CMD_VIAL_COMMAND, VIAL_GET_KEYBOARD_ID, NULL, 0, data, sizeof(data));
memcpy(vial_protocol, &data[0], sizeof(int));
memcpy(keyboard_uid, &data[sizeof(int)], sizeof(unsigned long long));
memcpy(vialrgb_flag, &data[sizeof(int) + sizeof(unsigned long long)], sizeof(unsigned char));
}
void QMKVialRGBController::CmdGetVialRGBInfo
(
unsigned short* vialrgb_protocol_version,
unsigned char* maximum_brightness
)
{
unsigned char data[sizeof(unsigned short) + sizeof(unsigned char)];
SendCommand(CMD_VIAL_COMMAND, VIAL_GET_KEYBOARD_ID, NULL, 0, data, sizeof(data));
memcpy(vialrgb_protocol_version, &data[0], sizeof(unsigned short));
memcpy(maximum_brightness, &data[sizeof(unsigned char)], sizeof(unsigned short));
}
void QMKVialRGBController::CmdGetViaProtocolVersion
(
unsigned short* via_protocol_version
)
{
SendCheckCommand(CMD_GET_PROTOCOL_VERSION, 0, NULL, 0, (unsigned char*)via_protocol_version, sizeof(unsigned int));
}
void QMKVialRGBController::CmdSendLEDs
(
unsigned short start_index,
unsigned char number_leds,
RGBColor* color_data
)
{
unsigned char data[30];
memcpy(&data[0], &start_index, sizeof(start_index));
memcpy(&data[2], &number_leds, sizeof(number_leds));
if(number_leds > 9)
{
number_leds = 9;
}
for(unsigned char led_index = 0; led_index < number_leds; led_index++)
{
/*-------------------------------------------------*\
| VialRGB sends direct packets in HSV for some |
| inexplicable reason, so do the RGB to HSV |
| conversion before sending |
\*-------------------------------------------------*/
hsv_t hsv_color;
rgb2hsv(color_data[led_index], &hsv_color);
data[3 + (led_index * 3)] = (unsigned char)((float)hsv_color.hue * (256.0f / 360.0f));
data[4 + (led_index * 3)] = hsv_color.saturation;
data[5 + (led_index * 3)] = hsv_color.value;
}
SendCheckCommand(CMD_LIGHTING_SET_VALUE, VIALRGB_DIRECT_FASTSET, data, sizeof(data), NULL, 0);
}
void QMKVialRGBController::CmdSetMode
(
unsigned short mode,
unsigned char speed,
unsigned char hue,
unsigned char sat,
unsigned char val
)
{
unsigned char data[6];
memcpy(&data[0], &mode, sizeof(unsigned short));
data[2] = speed;
data[3] = hue;
data[4] = sat;
data[5] = val;
SendCommand(CMD_LIGHTING_SET_VALUE, VIALRGB_SET_MODE, data, sizeof(data), NULL, 0);
}
int QMKVialRGBController::SendCommand
(
unsigned char cmd,
unsigned char subcmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
)
{
unsigned char usb_buf[MSG_LEN + 1];
memset(usb_buf, 0, sizeof(usb_buf));
usb_buf[0] = 0x00;
usb_buf[1] = cmd;
usb_buf[2] = subcmd;
memcpy(&usb_buf[3], data_in, data_in_size);
hid_write(dev, usb_buf, sizeof(usb_buf));
int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf)-1, 1000);
memcpy(data_out, &usb_buf[0], data_out_size);
return(bytes_received);
}
int QMKVialRGBController::SendCheckCommand
(
unsigned char cmd,
unsigned char subcmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
)
{
unsigned char usb_buf[MSG_LEN + 1];
memset(usb_buf, 0, sizeof(usb_buf));
usb_buf[0] = 0x00;
usb_buf[1] = cmd;
usb_buf[2] = subcmd;
memcpy(&usb_buf[3], data_in, data_in_size);
hid_write(dev, usb_buf, sizeof(usb_buf));
int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf)-1, 1000);
memcpy(data_out, &usb_buf[2], data_out_size);
return(bytes_received);
}
@@ -1,245 +1,245 @@
/*---------------------------------------------------------*\
| QMKVialRGBController.h |
| |
| Driver for VialRGB QMK Keyboard Protocol |
| |
| Adam Honse <calcprogrammer1@gmail.com) 29 Sep 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <hidapi.h>
#include "ResourceManager.h"
#include "QMKCommon.h"
#include "RGBController.h"
#define MSG_LEN 32
enum
{
CMD_GET_PROTOCOL_VERSION = 0x01,
CMD_GET_KEYBOARD_VALUE = 0x02,
CMD_SET_KEYBOARD_VALUE = 0x03,
CMD_VIA_DYNAMIC_KEYMAP_GET_KEYCODE = 0x04,
CMD_VIA_DYNAMIC_KEYMAP_SET_KEYCODE = 0x05,
CMD_VIA_DYNAMIC_KEYMAP_RESET = 0x06,
CMD_LIGHTING_SET_VALUE = 0x07,
CMD_LIGHTING_GET_VALUE = 0x08,
CMD_VIAL_COMMAND = 0xFE,
};
enum
{
VIAL_GET_KEYBOARD_ID = 0x00,
VIAL_GET_SIZE = 0x01,
VIAL_GET_DEFINITION = 0x02,
VIAL_GET_UNLOCK_STATUS = 0x05,
VIAL_UNLOCK_START = 0x06,
VIAL_UNLOCK_POLL = 0x07,
VIAL_LOCK = 0x08,
VIALRGB_GET_INFO = 0x40,
VIALRGB_GET_MODE = 0x41,
VIALRGB_GET_SUPPORTED = 0x42,
VIALRGB_GET_NUMBER_LEDS = 0x43,
VIALRGB_GET_LED_INFO = 0x44,
VIALRGB_SET_MODE = 0x41,
VIALRGB_DIRECT_FASTSET = 0x42,
};
enum
{
VIALRGB_EFFECT_OFF,
VIALRGB_EFFECT_DIRECT,
VIALRGB_EFFECT_SOLID_COLOR,
VIALRGB_EFFECT_ALPHAS_MODS,
VIALRGB_EFFECT_GRADIENT_UP_DOWN,
VIALRGB_EFFECT_GRADIENT_LEFT_RIGHT,
VIALRGB_EFFECT_BREATHING,
VIALRGB_EFFECT_BAND_SAT,
VIALRGB_EFFECT_BAND_VAL,
VIALRGB_EFFECT_BAND_PINWHEEL_SAT,
VIALRGB_EFFECT_BAND_PINWHEEL_VAL,
VIALRGB_EFFECT_BAND_SPIRAL_SAT,
VIALRGB_EFFECT_BAND_SPIRAL_VAL,
VIALRGB_EFFECT_CYCLE_ALL,
VIALRGB_EFFECT_CYCLE_LEFT_RIGHT,
VIALRGB_EFFECT_CYCLE_UP_DOWN,
VIALRGB_EFFECT_RAINBOW_MOVING_CHEVRON,
VIALRGB_EFFECT_CYCLE_OUT_IN,
VIALRGB_EFFECT_CYCLE_OUT_IN_DUAL,
VIALRGB_EFFECT_CYCLE_PINWHEEL,
VIALRGB_EFFECT_CYCLE_SPIRAL,
VIALRGB_EFFECT_DUAL_BEACON,
VIALRGB_EFFECT_RAINBOW_BEACON,
VIALRGB_EFFECT_RAINBOW_PINWHEELS,
VIALRGB_EFFECT_RAINDROPS,
VIALRGB_EFFECT_JELLYBEAN_RAINDROPS,
VIALRGB_EFFECT_HUE_BREATHING,
VIALRGB_EFFECT_HUE_PENDULUM,
VIALRGB_EFFECT_HUE_WAVE,
VIALRGB_EFFECT_TYPING_HEATMAP,
VIALRGB_EFFECT_DIGITAL_RAIN,
VIALRGB_EFFECT_SOLID_REACTIVE_SIMPLE,
VIALRGB_EFFECT_SOLID_REACTIVE,
VIALRGB_EFFECT_SOLID_REACTIVE_WIDE,
VIALRGB_EFFECT_SOLID_REACTIVE_MULTIWIDE,
VIALRGB_EFFECT_SOLID_REACTIVE_CROSS,
VIALRGB_EFFECT_SOLID_REACTIVE_MULTICROSS,
VIALRGB_EFFECT_SOLID_REACTIVE_NEXUS,
VIALRGB_EFFECT_SOLID_REACTIVE_MULTINEXUS,
VIALRGB_EFFECT_SPLASH,
VIALRGB_EFFECT_MULTISPLASH,
VIALRGB_EFFECT_SOLID_SPLASH,
VIALRGB_EFFECT_SOLID_MULTISPLASH,
VIALRGB_EFFECT_PIXEL_RAIN,
VIALRGB_EFFECT_PIXEL_FRACTAL,
VIALRGB_EFFECT_SKIP = 0xFFFF
};
class QMKVialRGBController
{
public:
QMKVialRGBController(hid_device *dev_handle, const char *path);
~QMKVialRGBController();
std::string GetLocation();
std::string GetName();
std::string GetSerial();
std::string GetVendor();
std::string GetVersion();
bool GetSupported();
unsigned short GetEffect(std::size_t effect_idx);
std::size_t GetEffectCount();
unsigned short GetKeycode(unsigned short led_index);
unsigned short GetLEDCount();
qmk_rgb_matrix_led_info GetLEDInfo(unsigned short led_index);
void GetMode
(
unsigned short* mode,
unsigned char* speed,
unsigned char* hue,
unsigned char* sat,
unsigned char* val
);
void SendLEDs
(
unsigned short number_leds,
RGBColor* color_data
);
void SetMode
(
unsigned short mode,
unsigned char speed,
unsigned char hue,
unsigned char sat,
unsigned char val
);
private:
hid_device* dev;
unsigned long long keyboard_uid;
std::vector<unsigned short> keycodes;
std::vector<qmk_rgb_matrix_led_info> led_info;
std::string location;
unsigned char maximum_brightness;
std::string name;
unsigned short number_leds;
std::string serial;
bool supported;
std::vector<unsigned short> supported_effects;
std::string vendor;
unsigned short via_protocol_version;
unsigned int vial_protocol_version;
unsigned short vialrgb_protocol_version;
unsigned char vialrgb_flag;
unsigned short CmdGetKeycode
(
unsigned char layer,
unsigned char row,
unsigned char col
);
qmk_rgb_matrix_led_info CmdGetLEDInfo
(
unsigned short led_index
);
void CmdGetMode
(
unsigned short* mode,
unsigned char* speed,
unsigned char* hue,
unsigned char* sat,
unsigned char* val
);
void CmdGetNumberLEDs
(
unsigned short* number_leds
);
void CmdGetSupportedEffects();
void CmdGetVialInfo
(
unsigned int* vial_protocol_version,
unsigned long long* keyboard_uid,
unsigned char* vialrgb_flag
);
void CmdGetVialRGBInfo
(
unsigned short* vialrgb_protocol_version,
unsigned char* maximum_brightness
);
void CmdGetViaProtocolVersion
(
unsigned short* via_protocol_version
);
void CmdSendLEDs
(
unsigned short start_index,
unsigned char number_leds,
RGBColor* color_data
);
void CmdSetMode
(
unsigned short mode,
unsigned char speed,
unsigned char hue,
unsigned char sat,
unsigned char val
);
int SendCommand
(
unsigned char cmd,
unsigned char subcmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
);
int SendCheckCommand
(
unsigned char cmd,
unsigned char subcmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
);
};
/*---------------------------------------------------------*\
| QMKVialRGBController.h |
| |
| Driver for VialRGB QMK Keyboard Protocol |
| |
| Adam Honse <calcprogrammer1@gmail.com) 29 Sep 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <hidapi.h>
#include "ResourceManager.h"
#include "QMKCommon.h"
#include "RGBController.h"
#define MSG_LEN 32
enum
{
CMD_GET_PROTOCOL_VERSION = 0x01,
CMD_GET_KEYBOARD_VALUE = 0x02,
CMD_SET_KEYBOARD_VALUE = 0x03,
CMD_VIA_DYNAMIC_KEYMAP_GET_KEYCODE = 0x04,
CMD_VIA_DYNAMIC_KEYMAP_SET_KEYCODE = 0x05,
CMD_VIA_DYNAMIC_KEYMAP_RESET = 0x06,
CMD_LIGHTING_SET_VALUE = 0x07,
CMD_LIGHTING_GET_VALUE = 0x08,
CMD_VIAL_COMMAND = 0xFE,
};
enum
{
VIAL_GET_KEYBOARD_ID = 0x00,
VIAL_GET_SIZE = 0x01,
VIAL_GET_DEFINITION = 0x02,
VIAL_GET_UNLOCK_STATUS = 0x05,
VIAL_UNLOCK_START = 0x06,
VIAL_UNLOCK_POLL = 0x07,
VIAL_LOCK = 0x08,
VIALRGB_GET_INFO = 0x40,
VIALRGB_GET_MODE = 0x41,
VIALRGB_GET_SUPPORTED = 0x42,
VIALRGB_GET_NUMBER_LEDS = 0x43,
VIALRGB_GET_LED_INFO = 0x44,
VIALRGB_SET_MODE = 0x41,
VIALRGB_DIRECT_FASTSET = 0x42,
};
enum
{
VIALRGB_EFFECT_OFF,
VIALRGB_EFFECT_DIRECT,
VIALRGB_EFFECT_SOLID_COLOR,
VIALRGB_EFFECT_ALPHAS_MODS,
VIALRGB_EFFECT_GRADIENT_UP_DOWN,
VIALRGB_EFFECT_GRADIENT_LEFT_RIGHT,
VIALRGB_EFFECT_BREATHING,
VIALRGB_EFFECT_BAND_SAT,
VIALRGB_EFFECT_BAND_VAL,
VIALRGB_EFFECT_BAND_PINWHEEL_SAT,
VIALRGB_EFFECT_BAND_PINWHEEL_VAL,
VIALRGB_EFFECT_BAND_SPIRAL_SAT,
VIALRGB_EFFECT_BAND_SPIRAL_VAL,
VIALRGB_EFFECT_CYCLE_ALL,
VIALRGB_EFFECT_CYCLE_LEFT_RIGHT,
VIALRGB_EFFECT_CYCLE_UP_DOWN,
VIALRGB_EFFECT_RAINBOW_MOVING_CHEVRON,
VIALRGB_EFFECT_CYCLE_OUT_IN,
VIALRGB_EFFECT_CYCLE_OUT_IN_DUAL,
VIALRGB_EFFECT_CYCLE_PINWHEEL,
VIALRGB_EFFECT_CYCLE_SPIRAL,
VIALRGB_EFFECT_DUAL_BEACON,
VIALRGB_EFFECT_RAINBOW_BEACON,
VIALRGB_EFFECT_RAINBOW_PINWHEELS,
VIALRGB_EFFECT_RAINDROPS,
VIALRGB_EFFECT_JELLYBEAN_RAINDROPS,
VIALRGB_EFFECT_HUE_BREATHING,
VIALRGB_EFFECT_HUE_PENDULUM,
VIALRGB_EFFECT_HUE_WAVE,
VIALRGB_EFFECT_TYPING_HEATMAP,
VIALRGB_EFFECT_DIGITAL_RAIN,
VIALRGB_EFFECT_SOLID_REACTIVE_SIMPLE,
VIALRGB_EFFECT_SOLID_REACTIVE,
VIALRGB_EFFECT_SOLID_REACTIVE_WIDE,
VIALRGB_EFFECT_SOLID_REACTIVE_MULTIWIDE,
VIALRGB_EFFECT_SOLID_REACTIVE_CROSS,
VIALRGB_EFFECT_SOLID_REACTIVE_MULTICROSS,
VIALRGB_EFFECT_SOLID_REACTIVE_NEXUS,
VIALRGB_EFFECT_SOLID_REACTIVE_MULTINEXUS,
VIALRGB_EFFECT_SPLASH,
VIALRGB_EFFECT_MULTISPLASH,
VIALRGB_EFFECT_SOLID_SPLASH,
VIALRGB_EFFECT_SOLID_MULTISPLASH,
VIALRGB_EFFECT_PIXEL_RAIN,
VIALRGB_EFFECT_PIXEL_FRACTAL,
VIALRGB_EFFECT_SKIP = 0xFFFF
};
class QMKVialRGBController
{
public:
QMKVialRGBController(hid_device *dev_handle, const char *path);
~QMKVialRGBController();
std::string GetLocation();
std::string GetName();
std::string GetSerial();
std::string GetVendor();
std::string GetVersion();
bool GetSupported();
unsigned short GetEffect(std::size_t effect_idx);
std::size_t GetEffectCount();
unsigned short GetKeycode(unsigned short led_index);
unsigned short GetLEDCount();
qmk_rgb_matrix_led_info GetLEDInfo(unsigned short led_index);
void GetMode
(
unsigned short* mode,
unsigned char* speed,
unsigned char* hue,
unsigned char* sat,
unsigned char* val
);
void SendLEDs
(
unsigned short number_leds,
RGBColor* color_data
);
void SetMode
(
unsigned short mode,
unsigned char speed,
unsigned char hue,
unsigned char sat,
unsigned char val
);
private:
hid_device* dev;
unsigned long long keyboard_uid;
std::vector<unsigned short> keycodes;
std::vector<qmk_rgb_matrix_led_info> led_info;
std::string location;
unsigned char maximum_brightness;
std::string name;
unsigned short number_leds;
std::string serial;
bool supported;
std::vector<unsigned short> supported_effects;
std::string vendor;
unsigned short via_protocol_version;
unsigned int vial_protocol_version;
unsigned short vialrgb_protocol_version;
unsigned char vialrgb_flag;
unsigned short CmdGetKeycode
(
unsigned char layer,
unsigned char row,
unsigned char col
);
qmk_rgb_matrix_led_info CmdGetLEDInfo
(
unsigned short led_index
);
void CmdGetMode
(
unsigned short* mode,
unsigned char* speed,
unsigned char* hue,
unsigned char* sat,
unsigned char* val
);
void CmdGetNumberLEDs
(
unsigned short* number_leds
);
void CmdGetSupportedEffects();
void CmdGetVialInfo
(
unsigned int* vial_protocol_version,
unsigned long long* keyboard_uid,
unsigned char* vialrgb_flag
);
void CmdGetVialRGBInfo
(
unsigned short* vialrgb_protocol_version,
unsigned char* maximum_brightness
);
void CmdGetViaProtocolVersion
(
unsigned short* via_protocol_version
);
void CmdSendLEDs
(
unsigned short start_index,
unsigned char number_leds,
RGBColor* color_data
);
void CmdSetMode
(
unsigned short mode,
unsigned char speed,
unsigned char hue,
unsigned char sat,
unsigned char val
);
int SendCommand
(
unsigned char cmd,
unsigned char subcmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
);
int SendCheckCommand
(
unsigned char cmd,
unsigned char subcmd,
unsigned char* data_in,
unsigned char data_in_size,
unsigned char* data_out,
unsigned char data_out_size
);
};
@@ -1,84 +1,84 @@
/*---------------------------------------------------------*\
| QMKVialRGBControllerDetect.cpp |
| |
| Detector for VialRGB QMK Keyboard Protocol |
| |
| Adam Honse <calcprogrammer1@gmail.com> 29 Sep 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <string>
#include <hidapi.h>
#include "Detector.h"
#include "QMKVialRGBController.h"
#include "RGBController_QMKVialRGB.h"
#include "SettingsManager.h"
/*-----------------------------------------------------*\
| USB IDs |
\*-----------------------------------------------------*/
#define RASPBERRY_PI_VID 0x2E8A
#define RASPBERRY_PI_500_PLUS_PID 0x0011
/*-----------------------------------------------------*\
| Usage and Usage Page |
\*-----------------------------------------------------*/
#define QMK_USAGE_PAGE 0xFF60
#define QMK_USAGE 0x61
void DetectQMKVialRGBControllers(hid_device_info *info, const std::string&)
{
hid_device *dev = hid_open_path(info->path);
if(dev)
{
QMKVialRGBController* controller = new QMKVialRGBController(dev, info->path);
if(controller->GetSupported())
{
RGBController_QMKVialRGB* rgb_controller = new RGBController_QMKVialRGB(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
else
{
delete controller;
}
}
}
void RegisterQMKVialRGBDetectors()
{
/*-------------------------------------------------*\
| Get QMKVialRGB settings |
\*-------------------------------------------------*/
json vial_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("QMKVialRGBDevices");
if(vial_settings.contains("devices"))
{
for(unsigned int device_idx = 0; device_idx < vial_settings["devices"].size(); device_idx++)
{
if( vial_settings["devices"][device_idx].contains("usb_pid")
&& vial_settings["devices"][device_idx].contains("usb_vid")
&& vial_settings["devices"][device_idx].contains("name"))
{
std::string usb_pid_str = vial_settings["devices"][device_idx]["usb_pid"];
std::string usb_vid_str = vial_settings["devices"][device_idx]["usb_vid"];
std::string name = vial_settings["devices"][device_idx]["name"];
/*-------------------------------------*\
| Parse hex string to integer |
\*-------------------------------------*/
unsigned short usb_pid = std::stoi(usb_pid_str, 0, 16);
unsigned short usb_vid = std::stoi(usb_vid_str, 0, 16);
REGISTER_DYNAMIC_HID_DETECTOR_PU(name, DetectQMKVialRGBControllers, usb_vid, usb_pid, QMK_USAGE_PAGE, QMK_USAGE);
}
}
}
}
REGISTER_DYNAMIC_DETECTOR("QMK VialRGB Devices", RegisterQMKVialRGBDetectors);
REGISTER_HID_DETECTOR_PU( "Raspberry Pi 500+", DetectQMKVialRGBControllers, RASPBERRY_PI_VID, RASPBERRY_PI_500_PLUS_PID, QMK_USAGE_PAGE, QMK_USAGE );
/*---------------------------------------------------------*\
| QMKVialRGBControllerDetect.cpp |
| |
| Detector for VialRGB QMK Keyboard Protocol |
| |
| Adam Honse <calcprogrammer1@gmail.com> 29 Sep 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <string>
#include <hidapi.h>
#include "Detector.h"
#include "QMKVialRGBController.h"
#include "RGBController_QMKVialRGB.h"
#include "SettingsManager.h"
/*-----------------------------------------------------*\
| USB IDs |
\*-----------------------------------------------------*/
#define RASPBERRY_PI_VID 0x2E8A
#define RASPBERRY_PI_500_PLUS_PID 0x0011
/*-----------------------------------------------------*\
| Usage and Usage Page |
\*-----------------------------------------------------*/
#define QMK_USAGE_PAGE 0xFF60
#define QMK_USAGE 0x61
void DetectQMKVialRGBControllers(hid_device_info *info, const std::string&)
{
hid_device *dev = hid_open_path(info->path);
if(dev)
{
QMKVialRGBController* controller = new QMKVialRGBController(dev, info->path);
if(controller->GetSupported())
{
RGBController_QMKVialRGB* rgb_controller = new RGBController_QMKVialRGB(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
else
{
delete controller;
}
}
}
void RegisterQMKVialRGBDetectors()
{
/*-------------------------------------------------*\
| Get QMKVialRGB settings |
\*-------------------------------------------------*/
json vial_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("QMKVialRGBDevices");
if(vial_settings.contains("devices"))
{
for(unsigned int device_idx = 0; device_idx < vial_settings["devices"].size(); device_idx++)
{
if( vial_settings["devices"][device_idx].contains("usb_pid")
&& vial_settings["devices"][device_idx].contains("usb_vid")
&& vial_settings["devices"][device_idx].contains("name"))
{
std::string usb_pid_str = vial_settings["devices"][device_idx]["usb_pid"];
std::string usb_vid_str = vial_settings["devices"][device_idx]["usb_vid"];
std::string name = vial_settings["devices"][device_idx]["name"];
/*-------------------------------------*\
| Parse hex string to integer |
\*-------------------------------------*/
unsigned short usb_pid = std::stoi(usb_pid_str, 0, 16);
unsigned short usb_vid = std::stoi(usb_vid_str, 0, 16);
REGISTER_DYNAMIC_HID_DETECTOR_PU(name, DetectQMKVialRGBControllers, usb_vid, usb_pid, QMK_USAGE_PAGE, QMK_USAGE);
}
}
}
}
REGISTER_DYNAMIC_DETECTOR("QMK VialRGB Devices", RegisterQMKVialRGBDetectors);
REGISTER_HID_DETECTOR_PU( "Raspberry Pi 500+", DetectQMKVialRGBControllers, RASPBERRY_PI_VID, RASPBERRY_PI_500_PLUS_PID, QMK_USAGE_PAGE, QMK_USAGE );
@@ -1,268 +1,268 @@
/*---------------------------------------------------------*\
| RGBController_QMKVialRGB.cpp |
| |
| RGBController for VialRGB QMK Keyboard Protocol |
| |
| Adam Honse <calcprogrammer1@gmail.com) 29 Sep 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "hsv.h"
#include "QMKKeycodes.h"
#include "RGBController_QMKVialRGB.h"
typedef struct
{
unsigned short value;
std::string name;
bool has_speed;
} vialrgb_mode;
#define VIALRGB_NUM_MODES (sizeof(vialrgb_modes) / sizeof(vialrgb_mode))
static const vialrgb_mode vialrgb_modes[] =
{
{ VIALRGB_EFFECT_OFF, "Off", false },
{ VIALRGB_EFFECT_DIRECT, "Direct", false },
{ VIALRGB_EFFECT_SOLID_COLOR, "Static", false },
{ VIALRGB_EFFECT_ALPHAS_MODS, "Alphas Mods", true },
{ VIALRGB_EFFECT_GRADIENT_UP_DOWN, "Gradient Up Down", true },
{ VIALRGB_EFFECT_GRADIENT_LEFT_RIGHT, "Gradient Left Right", true },
{ VIALRGB_EFFECT_BREATHING, "Breathing", true },
{ VIALRGB_EFFECT_BAND_SAT, "Band Sat", true },
{ VIALRGB_EFFECT_BAND_VAL, "Band Val", true },
{ VIALRGB_EFFECT_BAND_PINWHEEL_SAT, "Band Pinwheel Sat", true },
{ VIALRGB_EFFECT_BAND_PINWHEEL_VAL, "Band Pinwheel Val", true },
{ VIALRGB_EFFECT_BAND_SPIRAL_SAT, "Band Spiral Sat", true },
{ VIALRGB_EFFECT_BAND_SPIRAL_VAL, "Band Spiral Val", true },
{ VIALRGB_EFFECT_CYCLE_ALL, "Cycle All", true },
{ VIALRGB_EFFECT_CYCLE_LEFT_RIGHT, "Cycle Left Right", true },
{ VIALRGB_EFFECT_CYCLE_UP_DOWN, "Cycle Up Down", true },
{ VIALRGB_EFFECT_RAINBOW_MOVING_CHEVRON, "Rainbow Moving Chevron", true },
{ VIALRGB_EFFECT_CYCLE_OUT_IN, "Cycle Out In", true },
{ VIALRGB_EFFECT_CYCLE_OUT_IN_DUAL, "Cycle Out In Dual", true },
{ VIALRGB_EFFECT_CYCLE_PINWHEEL, "Cycle Pinwheel", true },
{ VIALRGB_EFFECT_CYCLE_SPIRAL, "Cycle Spiral", true },
{ VIALRGB_EFFECT_DUAL_BEACON, "Dual Beacon", true },
{ VIALRGB_EFFECT_RAINBOW_BEACON, "Rainbow Beacon", true },
{ VIALRGB_EFFECT_RAINBOW_PINWHEELS, "Rainbow Pinwheels", true },
{ VIALRGB_EFFECT_RAINDROPS, "Raindrops", true },
{ VIALRGB_EFFECT_JELLYBEAN_RAINDROPS, "Jellybean Raindrops", true },
{ VIALRGB_EFFECT_HUE_BREATHING, "Hue Breathing", true },
{ VIALRGB_EFFECT_HUE_PENDULUM, "Hue Pendulum", true },
{ VIALRGB_EFFECT_HUE_WAVE, "Hue Wave", true },
{ VIALRGB_EFFECT_TYPING_HEATMAP, "Typing Heatmap", true },
{ VIALRGB_EFFECT_DIGITAL_RAIN, "Digital Rain", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_SIMPLE, "Solid Reactive Simple", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE, "Solid Reactive", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_WIDE, "Solid Reactive Wide", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_MULTIWIDE, "Solid Reactive Multiwide", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_CROSS, "Solid Reactive Cross", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_MULTICROSS, "Solid Reactive Multicross", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_NEXUS, "Solid Reactive Nexus", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_MULTINEXUS, "Solid Reactive Multinexus", true },
{ VIALRGB_EFFECT_SPLASH, "Splash", true },
{ VIALRGB_EFFECT_MULTISPLASH, "Multisplash", true },
{ VIALRGB_EFFECT_SOLID_SPLASH, "Solid Splash", true },
{ VIALRGB_EFFECT_SOLID_MULTISPLASH, "Solid Multisplash", true },
{ VIALRGB_EFFECT_PIXEL_RAIN, "Pixel Rain", true },
{ VIALRGB_EFFECT_PIXEL_FRACTAL, "Pixel Fractal", true },
};
RGBController_QMKVialRGB::RGBController_QMKVialRGB(QMKVialRGBController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetName();
description = "QMK VialRGB Device";
vendor = controller->GetVendor();
location = controller->GetLocation();
serial = controller->GetSerial();
version = controller->GetVersion();
type = DEVICE_TYPE_KEYBOARD;
/*-----------------------------------------------------*\
| Read mode list |
\*-----------------------------------------------------*/
for(std::size_t effect_idx = 0; effect_idx < controller->GetEffectCount(); effect_idx++)
{
unsigned short mode_index = controller->GetEffect(effect_idx);
mode new_mode;
if(mode_index > VIALRGB_NUM_MODES)
{
continue;
}
new_mode.name = vialrgb_modes[mode_index].name;
new_mode.value = vialrgb_modes[mode_index].value;
if(new_mode.value == VIALRGB_EFFECT_DIRECT)
{
new_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR;
new_mode.color_mode = MODE_COLORS_PER_LED;
}
if(new_mode.value >= VIALRGB_EFFECT_SOLID_COLOR)
{
new_mode.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
new_mode.color_mode = MODE_COLORS_MODE_SPECIFIC;
new_mode.colors_min = 1;
new_mode.colors_max = 1;
new_mode.colors.resize(1);
}
if(vialrgb_modes[mode_index].has_speed)
{
new_mode.flags |= MODE_FLAG_HAS_SPEED;
new_mode.speed_min = 0;
new_mode.speed_max = 255;
new_mode.speed = 128;
}
modes.push_back(new_mode);
}
/*-----------------------------------------------------*\
| Read current mode |
\*-----------------------------------------------------*/
unsigned short cur_mode;
unsigned char cur_speed;
unsigned char cur_hue;
unsigned char cur_sat;
unsigned char cur_val;
controller->GetMode(&cur_mode, &cur_speed, &cur_hue, &cur_sat, &cur_val);
active_mode = cur_mode;
if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED)
{
modes[active_mode].speed = cur_speed;
}
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
{
hsv_t hsv_color;
hsv_color.hue = (unsigned int)((float)cur_hue * (360.0f / 256.0f));
hsv_color.saturation = cur_sat;
hsv_color.value = cur_val;
RGBColor rgb_color = hsv2rgb(&hsv_color);
modes[active_mode].colors[0] = rgb_color;
}
SetupZones();
}
RGBController_QMKVialRGB::~RGBController_QMKVialRGB()
{
delete controller;
}
void RGBController_QMKVialRGB::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++)
{
qmk_rgb_matrix_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++)
{
qmk_rgb_matrix_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_QMKVialRGB::ResizeZone(int /*zone*/, int /*new_size*/)
{
}
void RGBController_QMKVialRGB::DeviceUpdateLEDs()
{
controller->SendLEDs((unsigned short)colors.size(), colors.data());
}
void RGBController_QMKVialRGB::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_QMKVialRGB::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_QMKVialRGB::DeviceUpdateMode()
{
unsigned char hue = 0;
unsigned char sat = 0;
unsigned char val = 0;
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);
}
/*---------------------------------------------------------*\
| RGBController_QMKVialRGB.cpp |
| |
| RGBController for VialRGB QMK Keyboard Protocol |
| |
| Adam Honse <calcprogrammer1@gmail.com) 29 Sep 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "hsv.h"
#include "QMKKeycodes.h"
#include "RGBController_QMKVialRGB.h"
typedef struct
{
unsigned short value;
std::string name;
bool has_speed;
} vialrgb_mode;
#define VIALRGB_NUM_MODES (sizeof(vialrgb_modes) / sizeof(vialrgb_mode))
static const vialrgb_mode vialrgb_modes[] =
{
{ VIALRGB_EFFECT_OFF, "Off", false },
{ VIALRGB_EFFECT_DIRECT, "Direct", false },
{ VIALRGB_EFFECT_SOLID_COLOR, "Static", false },
{ VIALRGB_EFFECT_ALPHAS_MODS, "Alphas Mods", true },
{ VIALRGB_EFFECT_GRADIENT_UP_DOWN, "Gradient Up Down", true },
{ VIALRGB_EFFECT_GRADIENT_LEFT_RIGHT, "Gradient Left Right", true },
{ VIALRGB_EFFECT_BREATHING, "Breathing", true },
{ VIALRGB_EFFECT_BAND_SAT, "Band Sat", true },
{ VIALRGB_EFFECT_BAND_VAL, "Band Val", true },
{ VIALRGB_EFFECT_BAND_PINWHEEL_SAT, "Band Pinwheel Sat", true },
{ VIALRGB_EFFECT_BAND_PINWHEEL_VAL, "Band Pinwheel Val", true },
{ VIALRGB_EFFECT_BAND_SPIRAL_SAT, "Band Spiral Sat", true },
{ VIALRGB_EFFECT_BAND_SPIRAL_VAL, "Band Spiral Val", true },
{ VIALRGB_EFFECT_CYCLE_ALL, "Cycle All", true },
{ VIALRGB_EFFECT_CYCLE_LEFT_RIGHT, "Cycle Left Right", true },
{ VIALRGB_EFFECT_CYCLE_UP_DOWN, "Cycle Up Down", true },
{ VIALRGB_EFFECT_RAINBOW_MOVING_CHEVRON, "Rainbow Moving Chevron", true },
{ VIALRGB_EFFECT_CYCLE_OUT_IN, "Cycle Out In", true },
{ VIALRGB_EFFECT_CYCLE_OUT_IN_DUAL, "Cycle Out In Dual", true },
{ VIALRGB_EFFECT_CYCLE_PINWHEEL, "Cycle Pinwheel", true },
{ VIALRGB_EFFECT_CYCLE_SPIRAL, "Cycle Spiral", true },
{ VIALRGB_EFFECT_DUAL_BEACON, "Dual Beacon", true },
{ VIALRGB_EFFECT_RAINBOW_BEACON, "Rainbow Beacon", true },
{ VIALRGB_EFFECT_RAINBOW_PINWHEELS, "Rainbow Pinwheels", true },
{ VIALRGB_EFFECT_RAINDROPS, "Raindrops", true },
{ VIALRGB_EFFECT_JELLYBEAN_RAINDROPS, "Jellybean Raindrops", true },
{ VIALRGB_EFFECT_HUE_BREATHING, "Hue Breathing", true },
{ VIALRGB_EFFECT_HUE_PENDULUM, "Hue Pendulum", true },
{ VIALRGB_EFFECT_HUE_WAVE, "Hue Wave", true },
{ VIALRGB_EFFECT_TYPING_HEATMAP, "Typing Heatmap", true },
{ VIALRGB_EFFECT_DIGITAL_RAIN, "Digital Rain", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_SIMPLE, "Solid Reactive Simple", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE, "Solid Reactive", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_WIDE, "Solid Reactive Wide", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_MULTIWIDE, "Solid Reactive Multiwide", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_CROSS, "Solid Reactive Cross", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_MULTICROSS, "Solid Reactive Multicross", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_NEXUS, "Solid Reactive Nexus", true },
{ VIALRGB_EFFECT_SOLID_REACTIVE_MULTINEXUS, "Solid Reactive Multinexus", true },
{ VIALRGB_EFFECT_SPLASH, "Splash", true },
{ VIALRGB_EFFECT_MULTISPLASH, "Multisplash", true },
{ VIALRGB_EFFECT_SOLID_SPLASH, "Solid Splash", true },
{ VIALRGB_EFFECT_SOLID_MULTISPLASH, "Solid Multisplash", true },
{ VIALRGB_EFFECT_PIXEL_RAIN, "Pixel Rain", true },
{ VIALRGB_EFFECT_PIXEL_FRACTAL, "Pixel Fractal", true },
};
RGBController_QMKVialRGB::RGBController_QMKVialRGB(QMKVialRGBController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetName();
description = "QMK VialRGB Device";
vendor = controller->GetVendor();
location = controller->GetLocation();
serial = controller->GetSerial();
version = controller->GetVersion();
type = DEVICE_TYPE_KEYBOARD;
/*-----------------------------------------------------*\
| Read mode list |
\*-----------------------------------------------------*/
for(std::size_t effect_idx = 0; effect_idx < controller->GetEffectCount(); effect_idx++)
{
unsigned short mode_index = controller->GetEffect(effect_idx);
mode new_mode;
if(mode_index > VIALRGB_NUM_MODES)
{
continue;
}
new_mode.name = vialrgb_modes[mode_index].name;
new_mode.value = vialrgb_modes[mode_index].value;
if(new_mode.value == VIALRGB_EFFECT_DIRECT)
{
new_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR;
new_mode.color_mode = MODE_COLORS_PER_LED;
}
if(new_mode.value >= VIALRGB_EFFECT_SOLID_COLOR)
{
new_mode.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
new_mode.color_mode = MODE_COLORS_MODE_SPECIFIC;
new_mode.colors_min = 1;
new_mode.colors_max = 1;
new_mode.colors.resize(1);
}
if(vialrgb_modes[mode_index].has_speed)
{
new_mode.flags |= MODE_FLAG_HAS_SPEED;
new_mode.speed_min = 0;
new_mode.speed_max = 255;
new_mode.speed = 128;
}
modes.push_back(new_mode);
}
/*-----------------------------------------------------*\
| Read current mode |
\*-----------------------------------------------------*/
unsigned short cur_mode;
unsigned char cur_speed;
unsigned char cur_hue;
unsigned char cur_sat;
unsigned char cur_val;
controller->GetMode(&cur_mode, &cur_speed, &cur_hue, &cur_sat, &cur_val);
active_mode = cur_mode;
if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED)
{
modes[active_mode].speed = cur_speed;
}
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
{
hsv_t hsv_color;
hsv_color.hue = (unsigned int)((float)cur_hue * (360.0f / 256.0f));
hsv_color.saturation = cur_sat;
hsv_color.value = cur_val;
RGBColor rgb_color = hsv2rgb(&hsv_color);
modes[active_mode].colors[0] = rgb_color;
}
SetupZones();
}
RGBController_QMKVialRGB::~RGBController_QMKVialRGB()
{
delete controller;
}
void RGBController_QMKVialRGB::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++)
{
qmk_rgb_matrix_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++)
{
qmk_rgb_matrix_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_QMKVialRGB::ResizeZone(int /*zone*/, int /*new_size*/)
{
}
void RGBController_QMKVialRGB::DeviceUpdateLEDs()
{
controller->SendLEDs((unsigned short)colors.size(), colors.data());
}
void RGBController_QMKVialRGB::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_QMKVialRGB::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_QMKVialRGB::DeviceUpdateMode()
{
unsigned char hue = 0;
unsigned char sat = 0;
unsigned char val = 0;
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);
}
@@ -1,34 +1,34 @@
/*---------------------------------------------------------*\
| RGBController_QMKVialRGB.h |
| |
| RGBController for VialRGB QMK Keyboard Protocol |
| |
| Adam Honse <calcprogrammer1@gmail.com) 29 Sep 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "QMKVialRGBController.h"
#include "RGBController.h"
class RGBController_QMKVialRGB : public RGBController
{
public:
RGBController_QMKVialRGB(QMKVialRGBController* controller_ptr);
~RGBController_QMKVialRGB();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
QMKVialRGBController* controller;
};
/*---------------------------------------------------------*\
| RGBController_QMKVialRGB.h |
| |
| RGBController for VialRGB QMK Keyboard Protocol |
| |
| Adam Honse <calcprogrammer1@gmail.com) 29 Sep 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "QMKVialRGBController.h"
#include "RGBController.h"
class RGBController_QMKVialRGB : public RGBController
{
public:
RGBController_QMKVialRGB(QMKVialRGBController* controller_ptr);
~RGBController_QMKVialRGB();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
QMKVialRGBController* controller;
};