Compare commits
115
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e81e26fcc | ||
|
|
b97b429684 | ||
|
|
9f06fc63a1 | ||
|
|
d2dd9dcc73 | ||
|
|
6fbcf62d76 | ||
|
|
1a6943fe17 | ||
|
|
cde2343d09 | ||
|
|
c746b30287 | ||
|
|
3582b96dd9 | ||
|
|
d258065e36 | ||
|
|
7627466891 | ||
|
|
c476ee46f8 | ||
|
|
6fc68f4e62 | ||
|
|
75bac1fd4e | ||
|
|
94042375a7 | ||
|
|
103306c6c8 | ||
|
|
6d37d262ea | ||
|
|
473331dd3a | ||
|
|
bc9d03ef48 | ||
|
|
740c88fe0c | ||
|
|
684ef09168 | ||
|
|
e3b08fc15d | ||
|
|
5c931a61b4 | ||
|
|
ad7534d00d | ||
|
|
c81f8699f1 | ||
|
|
2af03eb0dc | ||
|
|
5064ba3f1d | ||
|
|
c5abe47859 | ||
|
|
8fdd0e40f8 | ||
|
|
a6a07ba0d7 | ||
|
|
614fef4189 | ||
|
|
d3e7dba2b7 | ||
|
|
39343c3e9e | ||
|
|
73865fec59 | ||
|
|
b508818f23 | ||
|
|
bef8ae0a2f | ||
|
|
cce2d41500 | ||
|
|
175f3ed338 | ||
|
|
c2cc7c6163 | ||
|
|
8fee041fad | ||
|
|
dfc1f8524a | ||
|
|
752bd2e35a | ||
|
|
01fe0b4630 | ||
|
|
e7f8bc0136 | ||
|
|
370918e07f | ||
|
|
133966677b | ||
|
|
44f84e4781 | ||
|
|
a8354ee046 | ||
|
|
4550e3073a | ||
|
|
abe7a49092 | ||
|
|
82a486bfb6 | ||
|
|
ad06e733f8 | ||
|
|
4306603a28 | ||
|
|
554f834a90 | ||
|
|
7236bec61f | ||
|
|
c799f1bfe9 | ||
|
|
f15c3c1420 | ||
|
|
aa67045330 | ||
|
|
736640f38d | ||
|
|
e9caf663bc | ||
|
|
8afbea53de | ||
|
|
c3d496ad5b | ||
|
|
06501f528d | ||
|
|
f6a678c1a4 | ||
|
|
30690d0ebd | ||
|
|
7a830cddf9 | ||
|
|
3dc1365d39 | ||
|
|
b03cd94ae8 | ||
|
|
bbf348d273 | ||
|
|
dce48eb415 | ||
|
|
decf14635d | ||
|
|
db4f49bbba | ||
|
|
26824fcb6e | ||
|
|
8afad91b33 | ||
|
|
69de0b13c4 | ||
|
|
967153bc10 | ||
|
|
971d9be1bd | ||
|
|
7a3faef886 | ||
|
|
f67030fcc7 | ||
|
|
7353e99449 | ||
|
|
91d9597c89 | ||
|
|
2eb569912b | ||
|
|
9410d9c066 | ||
|
|
29d8788e25 | ||
|
|
0304c50bc6 | ||
|
|
dd22150cfe | ||
|
|
07e7feca56 | ||
|
|
a93eec8f50 | ||
|
|
88bad762e5 | ||
|
|
18dfe6ca8b | ||
|
|
96dfa7009f | ||
|
|
92a9d8ad11 | ||
|
|
2ab9094b41 | ||
|
|
f6154ccbc4 | ||
|
|
0be01dde19 | ||
|
|
e113e91423 | ||
|
|
28c63b8b9d | ||
|
|
c5aac4c44f | ||
|
|
42e7f2a5c3 | ||
|
|
ae9ae18d31 | ||
|
|
f89cdac198 | ||
|
|
e8f345ff77 | ||
|
|
aff0610df9 | ||
|
|
9199d062d1 | ||
|
|
0ec4aaa629 | ||
|
|
9fca99b4ef | ||
|
|
14853895f3 | ||
|
|
d79a400c8c | ||
|
|
f741385e43 | ||
|
|
840bd92afb | ||
|
|
96e1fb0b10 | ||
|
|
388557e5f9 | ||
|
|
3f32817272 | ||
|
|
056b383536 | ||
|
|
0691f40588 |
+8
-20
@@ -14,9 +14,9 @@
|
||||
# run only if manually started to save CI minutes if #
|
||||
# GitLab hosted runners #
|
||||
# * For commits to the CalcProgrammer1/OpenRGB repository, #
|
||||
# automatically run all jobs on the default branch, #
|
||||
# otherwise run them if the source is part of a merge #
|
||||
# request #
|
||||
# automatically run all jobs on the default branch or #
|
||||
# tag builds, otherwise run them if the source is part #
|
||||
# of a merge request #
|
||||
#-----------------------------------------------------------#
|
||||
.downstream_rules:
|
||||
rules:
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
.upstream_rules:
|
||||
rules:
|
||||
- if: '$CI_PROJECT_PATH == "CalcProgrammer1/OpenRGB" && ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_PIPELINE_SOURCE == "merge_request_event")'
|
||||
- if: '$CI_PROJECT_PATH == "CalcProgrammer1/OpenRGB" && ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG || $CI_PIPELINE_SOURCE == "merge_request_event")'
|
||||
when: on_success
|
||||
- !reference [.downstream_rules, rules]
|
||||
|
||||
@@ -516,10 +516,7 @@ before_script:
|
||||
- macos
|
||||
stage: build
|
||||
script:
|
||||
- eval $(/opt/homebrew/bin/brew shellenv)
|
||||
- /opt/homebrew/opt/qt@5/bin/qmake OpenRGB.pro
|
||||
- make -j16
|
||||
- /opt/homebrew/opt/qt@5/bin/macdeployqt OpenRGB.app -codesign=OpenRGB
|
||||
- ./scripts/build-macos.sh qt5 arm
|
||||
|
||||
artifacts:
|
||||
name: "${CI_PROJECT_NAME}_MacOS_ARM64_${CI_COMMIT_SHORT_SHA}"
|
||||
@@ -535,10 +532,7 @@ before_script:
|
||||
- macos
|
||||
stage: build
|
||||
script:
|
||||
- eval $(/opt/homebrew/bin/brew shellenv)
|
||||
- qmake OpenRGB.pro
|
||||
- make -j16
|
||||
- macdeployqt OpenRGB.app -codesign=OpenRGB
|
||||
- ./scripts/build-macos.sh qt6 arm
|
||||
|
||||
artifacts:
|
||||
name: "${CI_PROJECT_NAME}_MacOS_ARM64_Qt6_${CI_COMMIT_SHORT_SHA}"
|
||||
@@ -554,10 +548,7 @@ before_script:
|
||||
- macos
|
||||
stage: build
|
||||
script:
|
||||
- eval $(/usr/local/bin/brew shellenv)
|
||||
- arch -x86_64 /usr/local/opt/qt@5/bin/qmake OpenRGB.pro
|
||||
- arch -x86_64 make -j16
|
||||
- arch -x86_64 /usr/local/opt/qt@5/bin/macdeployqt OpenRGB.app -codesign=OpenRGB
|
||||
- ./scripts/build-macos.sh qt5 intel
|
||||
|
||||
artifacts:
|
||||
name: "${CI_PROJECT_NAME}_MacOS_Intel_${CI_COMMIT_SHORT_SHA}"
|
||||
@@ -573,10 +564,7 @@ before_script:
|
||||
- macos
|
||||
stage: build
|
||||
script:
|
||||
- eval $(/usr/local/bin/brew shellenv)
|
||||
- arch -x86_64 /usr/local/bin/qmake OpenRGB.pro
|
||||
- arch -x86_64 make -j16
|
||||
- arch -x86_64 /usr/local/bin/macdeployqt OpenRGB.app -codesign=OpenRGB
|
||||
- ./scripts/build-macos.sh qt6 intel
|
||||
|
||||
artifacts:
|
||||
name: "${CI_PROJECT_NAME}_MacOS_Intel_Qt6_${CI_COMMIT_SHORT_SHA}"
|
||||
|
||||
@@ -69,3 +69,7 @@ OpenRGB is written in C++, uses the Qt framework for UI, and uses the QMake buil
|
||||
|
||||
Translation files are located in [`OpenRGB/qt/i18n/`](https://gitlab.com/CalcProgrammer1/OpenRGB/-/tree/master/qt/i18n), where languages are formatted using ISO 639-1 format: `OpenRGB_xx_XX.ts` — `xx_XX` representing the language code.
|
||||
In order to translate a file, you need to [fork](https://gitlab.com/CalcProgrammer1/OpenRGB/-/forks/new) the project, create a new file for your language with `lupdate` (or edit an exisiting one), edit the file with `qtlinguist`, commit, push, and create a merge request.
|
||||
|
||||
## AI Guidelines
|
||||
|
||||
OpenRGB is an open source project developed by humans for humans. As a general rule, AI generated submissions are not permitted. The licensing behind AI generated code is problematic. If you choose to use AI for assistance in your development process, we can't stop you, but do not credit the AI in commits (no AI authorship/co-authorship). Ultimately, you as a human developer are responsible for the code you submit and it is expected that any code you submit you fully understand and have manually vetted before submission. Merge requests that appear to be straight from an AI output, commits with AI tool authorship or co-authorship tags, or otherwise AI generated submissions are subject to closure.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define AURA_ROG_GLADIUS_II_WIRELESS_1_PID 0x189E
|
||||
#define AURA_ROG_GLADIUS_II_WIRELESS_2_PID 0x18A0
|
||||
#define AURA_ROG_GLADIUS_III_PID 0x197B
|
||||
#define AURA_ROG_GLADIUS_III_CORE_PID 0x1C8D
|
||||
#define AURA_ROG_GLADIUS_III_WIRELESS_USB_PID 0x197D
|
||||
#define AURA_ROG_GLADIUS_III_WIRELESS_2_4_PID 0x197F
|
||||
#define AURA_ROG_GLADIUS_III_WIRELESS_BT_PID 0x1981
|
||||
@@ -217,6 +218,20 @@ static std::map<int,mouse_type> aura_mouse_devices =
|
||||
{ AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY }
|
||||
}
|
||||
},
|
||||
{
|
||||
AURA_ROG_GLADIUS_III_CORE_PID, // ROG Gladius III Core
|
||||
{
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
100,
|
||||
false,
|
||||
1,
|
||||
false,
|
||||
{ AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL },
|
||||
{ AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE }
|
||||
}
|
||||
},
|
||||
{
|
||||
AURA_ROG_GLADIUS_III_WIRELESS_USB_PID, // ROG Gladius III Wireless USB
|
||||
{
|
||||
|
||||
+4
-1
@@ -19,7 +19,10 @@ AsusAuraRyuoAIOController::AsusAuraRyuoAIOController(hid_device* dev_handle, con
|
||||
| Manually adding device info for now |
|
||||
| TODO: Implement config table accurately |
|
||||
\*-----------------------------------------------------*/
|
||||
device_info.push_back({0x00, 0x00, 12, 0, AuraDeviceType::FIXED});
|
||||
uint8_t leds = (dev_name.find("Ryujin") != std::string::npos) ? 5 : 12;
|
||||
|
||||
device_info.push_back({0x00, 0x00, leds, 0, AuraDeviceType::FIXED});
|
||||
|
||||
}
|
||||
|
||||
AsusAuraRyuoAIOController::~AsusAuraRyuoAIOController()
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
#define AURA_TERMINAL_PID 0x1889
|
||||
#define ROG_STRIX_LC120_PID 0x879E
|
||||
#define AURA_RYUO_AIO_PID 0x1887
|
||||
#define AURA_RYUJIN_AIO_PID 0x18AE
|
||||
#define ASUS_ROG_ALLY_PID 0x1ABE
|
||||
#define ASUS_ROG_ALLY_X_PID 0x1B4C
|
||||
|
||||
@@ -393,6 +394,7 @@ REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Origin COD",
|
||||
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Wireless", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_WIRELESS_1_PID, 1, 0xFF13);
|
||||
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Wireless", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_WIRELESS_2_PID, 2, 0xFF01);
|
||||
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_PID, 0, 0xFF01);
|
||||
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Core", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_CORE_PID, 0, 0xFF01);
|
||||
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless USB", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_USB_PID, 0, 0xFF01);
|
||||
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless 2.4Ghz", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_2_4_PID, 0, 0xFF01);
|
||||
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless Bluetooth", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_BT_PID, 0, 0xFF01);
|
||||
@@ -453,6 +455,7 @@ REGISTER_HID_DETECTOR_PU("ASUS ROG PG32UQ", DetectAs
|
||||
REGISTER_HID_DETECTOR ("ASUS ROG AURA Terminal", DetectAsusAuraUSBTerminal, AURA_USB_VID, AURA_TERMINAL_PID);
|
||||
REGISTER_HID_DETECTOR_PU ("ASUS ROG Strix LC", DetectAsusAuraUSBROGStrixLC, AURA_USB_VID, ROG_STRIX_LC120_PID, 0x00FF, 1);
|
||||
REGISTER_HID_DETECTOR_PU ("ASUS ROG Ryuo AIO", DetectAsusAuraUSBRyuoAIO, AURA_USB_VID, AURA_RYUO_AIO_PID, 0xFF72, 0x00A1);
|
||||
REGISTER_HID_DETECTOR_PU ("ASUS ROG Ryujin AIO", DetectAsusAuraUSBRyuoAIO, AURA_USB_VID, AURA_RYUJIN_AIO_PID, 0xFF72, 0x00A1);
|
||||
REGISTER_HID_DETECTOR_I ("ASUS ROG Throne", DetectAsusAuraUSBHeadsetStand, AURA_USB_VID, AURA_ROG_THRONE_PID, 0);
|
||||
REGISTER_HID_DETECTOR_I ("ASUS ROG Throne QI", DetectAsusAuraUSBHeadsetStand, AURA_USB_VID, AURA_ROG_THRONE_QI_PID, 0);
|
||||
REGISTER_HID_DETECTOR_I ("ASUS ROG Throne QI GUNDAM", DetectAsusAuraUSBHeadsetStand, AURA_USB_VID, AURA_ROG_THRONE_QI_GUNDAM_PID, 0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*---------------------------------------------------------*\
|
||||
/*---------------------------------------------------------*\
|
||||
| ColorfulGPUControllerDetect.cpp |
|
||||
| |
|
||||
| Detector for Colorful GPU |
|
||||
@@ -48,7 +48,7 @@ REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Advanced OC 12G L-V", Detec
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA106_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3070_ADVANCED_OCV, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060TI_ADVANCED_OC, 0x61);
|
||||
@@ -72,6 +72,9 @@ REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", Detec
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4080_ULTRAW_OCV2, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4090 Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4090_ADVANCED_OCV, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4090 Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4090_ADVANCED_OCV2, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ti Ultra W DUO OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060_ULTRAW_OC, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060_ULTRAW_OC_2, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ti Ultra W DUO OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ti Ultra W DUO OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC_2, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5070_ULTRAW_OCV, 0x61);
|
||||
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5070_ULTRAW_OCV2, 0x61);
|
||||
@@ -26,6 +26,7 @@ CorsairDRAMController::CorsairDRAMController(i2c_smbus_interface *bus, corsair_d
|
||||
this->bus = bus;
|
||||
this->dev = dev;
|
||||
device_index = 0;
|
||||
direct_mode = true;
|
||||
pid = 0;
|
||||
vid = 0;
|
||||
protocol_version = 0;
|
||||
@@ -132,13 +133,79 @@ void CorsairDRAMController::SetColorsPerLED(RGBColor* colors)
|
||||
| bytes, use a second block write to the second |
|
||||
| block write address for the remaining data |
|
||||
\*-------------------------------------------------*/
|
||||
bus->i2c_smbus_write_block_data(dev, CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_1, 32, direct_packet);
|
||||
s32 ret = bus->i2c_smbus_write_block_data(dev, CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_1, 32, direct_packet);
|
||||
|
||||
if(direct_packet_size > 32)
|
||||
if((ret >= 0) && (direct_packet_size > 32))
|
||||
{
|
||||
bus->i2c_smbus_write_block_data(dev, CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_2, direct_packet_size - 32, direct_packet + 32);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Corsair DRAM supports an alternate means of |
|
||||
| writing block data without using SMBus block |
|
||||
| operations. If block operations are not |
|
||||
| available, fall back to this scheme. |
|
||||
| |
|
||||
| Blocks are split up into word data writes. |
|
||||
| Some data bytes are packed into the lower nibble |
|
||||
| of the register address byte. |
|
||||
\*-------------------------------------------------*/
|
||||
if(ret < 0)
|
||||
{
|
||||
unsigned int block_index = 0;
|
||||
bool even_frame = true;
|
||||
bool first_frame = true;
|
||||
|
||||
while(block_index < direct_packet_size)
|
||||
{
|
||||
unsigned char reg_value_0 = 0xA0;
|
||||
unsigned char reg_value_1 = 0x00;
|
||||
unsigned short word_value_0 = 0;
|
||||
unsigned short word_value_1 = 0;
|
||||
|
||||
if(block_index == 0)
|
||||
{
|
||||
reg_value_0 = 0x90;
|
||||
}
|
||||
|
||||
word_value_0 = (direct_packet[block_index]);
|
||||
block_index++;
|
||||
|
||||
if(block_index < direct_packet_size)
|
||||
{
|
||||
word_value_0 |= (direct_packet[block_index] << 8);
|
||||
block_index++;
|
||||
}
|
||||
|
||||
if(block_index < direct_packet_size)
|
||||
{
|
||||
reg_value_1 = 0xA0;
|
||||
reg_value_0 |= (direct_packet[block_index] & 0x0F);
|
||||
reg_value_1 |= (direct_packet[block_index] & 0xF0) >> 4;
|
||||
block_index++;
|
||||
}
|
||||
|
||||
if(block_index < direct_packet_size)
|
||||
{
|
||||
word_value_1 = (direct_packet[block_index]);
|
||||
block_index++;
|
||||
}
|
||||
|
||||
if(block_index < direct_packet_size)
|
||||
{
|
||||
word_value_1 |= (direct_packet[block_index] << 8);
|
||||
block_index++;
|
||||
}
|
||||
|
||||
bus->i2c_smbus_write_word_data(dev, reg_value_0, word_value_0);
|
||||
|
||||
if(reg_value_1 > 0)
|
||||
{
|
||||
bus->i2c_smbus_write_word_data(dev, reg_value_1, word_value_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Remember to delete the data buffer |
|
||||
\*-------------------------------------------------*/
|
||||
|
||||
@@ -24,18 +24,15 @@ using namespace std::chrono_literals;
|
||||
|
||||
bool TestForCorsairDRAMController(i2c_smbus_interface *bus, unsigned char address)
|
||||
{
|
||||
int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE);
|
||||
|
||||
LOG_DEBUG("[%s] Trying address %02X", CORSAIR_DRAM_NAME, address);
|
||||
|
||||
int res = bus->i2c_smbus_read_byte_data(address, 0x43);
|
||||
|
||||
if(res < 0)
|
||||
{
|
||||
LOG_DEBUG("[%s] Failed: res was %04X", CORSAIR_DRAM_NAME, res);
|
||||
return false;
|
||||
}
|
||||
|
||||
res = bus->i2c_smbus_read_byte_data(address, 0x43);
|
||||
|
||||
if(!(res == 0x1A || res == 0x1B || res == 0x1C))
|
||||
{
|
||||
LOG_DEBUG("[%s] Failed: expected 0x1A, 0x1B, or 0x1C, got %04X", CORSAIR_DRAM_NAME, res);
|
||||
@@ -44,9 +41,9 @@ bool TestForCorsairDRAMController(i2c_smbus_interface *bus, unsigned char addres
|
||||
|
||||
res = bus->i2c_smbus_read_byte_data(address, 0x44);
|
||||
|
||||
if(!(res == 0x03 || res == 0x04))
|
||||
if(!(res == 0x01 || res == 0x03 || res == 0x04))
|
||||
{
|
||||
LOG_DEBUG("[%s] Failed: expected 0x03 or 0x04, got %04X", CORSAIR_DRAM_NAME, res);
|
||||
LOG_DEBUG("[%s] Failed: expected 0x01, 0x03, or 0x04, got %04X", CORSAIR_DRAM_NAME, res);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,16 @@ void RGBController_CorsairICueLink::SetupZones()
|
||||
lcd_zone.leds_min = controller->GetEndpoints()[lcd_idx]->led_channels;
|
||||
lcd_zone.leds_max = lcd_zone.leds_min;
|
||||
lcd_zone.leds_count = lcd_zone.leds_min;
|
||||
|
||||
zones.push_back(lcd_zone);
|
||||
|
||||
for(unsigned int led_idx = 0; led_idx < lcd_zone.leds_count; led_idx++)
|
||||
{
|
||||
led new_led;
|
||||
new_led.name = "LED " + std::to_string(led_idx + 1);
|
||||
|
||||
leds.push_back(new_led);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@ CorsairPeripheralV2Controller::CorsairPeripheralV2Controller(hid_device* dev_han
|
||||
write_cmd = CORSAIR_V2_WRITE_WIRELESS_ID;
|
||||
pid = GetAddress(0x12);
|
||||
break;
|
||||
|
||||
case CORSAIR_K57_RGB_WIRED_PID:
|
||||
write_cmd = 0x80;
|
||||
light_ctrl = CORSAIR_V2_LIGHT_CTRL1;
|
||||
skip_reads = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
@@ -93,14 +99,17 @@ CorsairPeripheralV2Controller::CorsairPeripheralV2Controller(hid_device* dev_han
|
||||
| If lighting control endpoint 2 is unavailable |
|
||||
| then use endpoint 1. |
|
||||
\*---------------------------------------------------------*/
|
||||
result = StartTransaction(0);
|
||||
if(result > 0)
|
||||
if(light_ctrl == CORSAIR_V2_LIGHT_CTRL2)
|
||||
{
|
||||
light_ctrl = CORSAIR_V2_LIGHT_CTRL1;
|
||||
StartTransaction(0);
|
||||
result = StartTransaction(0);
|
||||
if(result > 0)
|
||||
{
|
||||
light_ctrl = CORSAIR_V2_LIGHT_CTRL1;
|
||||
StartTransaction(0);
|
||||
}
|
||||
StopTransaction(0);
|
||||
LOG_DEBUG("[%s] Lighting Endpoint set to %02X", device_name.c_str(), light_ctrl);
|
||||
}
|
||||
StopTransaction(0);
|
||||
LOG_DEBUG("[%s] Lighting Endpoint set to %02X", device_name.c_str(), light_ctrl);
|
||||
}
|
||||
|
||||
CorsairPeripheralV2Controller::~CorsairPeripheralV2Controller()
|
||||
@@ -171,7 +180,11 @@ void CorsairPeripheralV2Controller::SetRenderMode(corsair_v2_device_mode mode)
|
||||
buffer[5] = mode;
|
||||
|
||||
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
|
||||
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
|
||||
|
||||
if(!skip_reads)
|
||||
{
|
||||
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
void CorsairPeripheralV2Controller::LightingControl(uint8_t opt1)
|
||||
@@ -191,7 +204,11 @@ void CorsairPeripheralV2Controller::LightingControl(uint8_t opt1)
|
||||
buffer[5] = 0x00;
|
||||
|
||||
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
|
||||
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
|
||||
|
||||
if(!skip_reads)
|
||||
{
|
||||
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CorsairPeripheralV2Controller::GetKeyboardLayout()
|
||||
@@ -240,7 +257,11 @@ unsigned char CorsairPeripheralV2Controller::StartTransaction(uint8_t opt1)
|
||||
buffer[4] = light_ctrl;
|
||||
|
||||
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
|
||||
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
|
||||
|
||||
if(!skip_reads)
|
||||
{
|
||||
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
|
||||
}
|
||||
|
||||
return buffer[2];
|
||||
}
|
||||
@@ -257,11 +278,20 @@ void CorsairPeripheralV2Controller::StopTransaction(uint8_t opt1)
|
||||
buffer[4] = opt1;
|
||||
|
||||
hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE);
|
||||
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
|
||||
|
||||
if(!skip_reads)
|
||||
{
|
||||
hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
void CorsairPeripheralV2Controller::ClearPacketBuffer()
|
||||
{
|
||||
if(skip_reads)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t result = 0;
|
||||
uint8_t buffer[CORSAIR_V2_PACKET_SIZE];
|
||||
|
||||
@@ -304,7 +334,11 @@ void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size)
|
||||
memcpy(&buffer[offset1], &data[0], copy_bytes);
|
||||
|
||||
hid_write(dev, buffer, pkt_sze);
|
||||
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT);
|
||||
|
||||
if(!skip_reads)
|
||||
{
|
||||
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT);
|
||||
}
|
||||
|
||||
remaining -= copy_bytes;
|
||||
buffer[2] = CORSAIR_V2_CMD_BLK_WN;
|
||||
@@ -325,7 +359,11 @@ void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size)
|
||||
memcpy(&buffer[offset2], &data[index], copy_bytes);
|
||||
|
||||
hid_write(dev, buffer, pkt_sze);
|
||||
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT);
|
||||
|
||||
if(!skip_reads)
|
||||
{
|
||||
hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT);
|
||||
}
|
||||
|
||||
remaining -= copy_bytes;
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ private:
|
||||
|
||||
uint8_t write_cmd = CORSAIR_V2_WRITE_WIRED_ID;
|
||||
uint16_t pkt_sze = CORSAIR_V2_WRITE_SIZE;
|
||||
bool skip_reads = false;
|
||||
std::string firmware_version;
|
||||
std::string location;
|
||||
};
|
||||
|
||||
@@ -57,6 +57,7 @@ void DetectCorsairV2SoftwareControllers(hid_device_info* info, const std::string
|
||||
| Keyboards |
|
||||
\*-----------------------------------------------------------------------------------------------------*/
|
||||
REGISTER_HID_DETECTOR_IP("Corsair K55 RGB PRO", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K55_RGB_PRO_PID, 1, 0xFF42);
|
||||
REGISTER_HID_DETECTOR_IP("Corsair K57 RGB (Wired)", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K57_RGB_WIRED_PID, 1, 0xFF42);
|
||||
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_PID, 1, 0xFF42);
|
||||
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO Low Profile", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_LP_PID, 1, 0xFF42);
|
||||
REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO TKL Black", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_TKL_B_PID, 1, 0xFF42);
|
||||
|
||||
@@ -48,6 +48,31 @@ std::vector<unsigned int> corsair_full_size_values =
|
||||
/*-------------------------------------------------------------------------*\
|
||||
| KEYMAPS |
|
||||
\*-------------------------------------------------------------------------*/
|
||||
keyboard_keymap_overlay_values corsair_K57_layout
|
||||
{
|
||||
KEYBOARD_SIZE::KEYBOARD_SIZE_FULL,
|
||||
{
|
||||
corsair_full_size_values,
|
||||
{
|
||||
/* Add more regional layout fixes here */
|
||||
}
|
||||
},
|
||||
{
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
| Edit Keys |
|
||||
| Zone, Row, Column, Value, Name, Alternate Name, OpCode |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
{ 0, 0, 17, 0, "Power/Wireless Indicator", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, // Insert Profile into new row
|
||||
{ 0, 0, 18, 1, "Lock/Macro Indicator", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Light key
|
||||
{ 0, 1, 0, 131, "Key: G1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G1 key
|
||||
{ 0, 2, 0, 132, "Key: G2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G2 key
|
||||
{ 0, 3, 0, 133, "Key: G3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G3 key
|
||||
{ 0, 4, 0, 134, "Key: G4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G4 key
|
||||
{ 0, 5, 0, 135, "Key: G5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G5 key
|
||||
{ 0, 6, 0, 136, "Key: G6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G6 key
|
||||
}
|
||||
};
|
||||
|
||||
keyboard_keymap_overlay_values corsair_K60_layout
|
||||
{
|
||||
KEYBOARD_SIZE::KEYBOARD_SIZE_FULL,
|
||||
@@ -116,12 +141,14 @@ keyboard_keymap_overlay_values corsair_k70_pro_layout
|
||||
{ 0, 0, 0, 128, "Profile", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, // Insert Profile into new row
|
||||
{ 0, 0, 1, 113, "Light", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Light key
|
||||
{ 0, 0, 2, 114, "Lock", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lock Key
|
||||
{ 0, 0, 10, 191, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Logo
|
||||
{ 0, 0, 10, 138, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Logo
|
||||
{ 0, 0, 18, 102, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Mute Key
|
||||
{ 0, 1, 17, 123, KEY_EN_MEDIA_STOP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Stop Key
|
||||
{ 0, 1, 18, 126, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Previous Track Key
|
||||
{ 0, 1, 19, 124, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Play Pause Key
|
||||
{ 0, 1, 20, 125, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Next Tack Key
|
||||
{ 0, 6, 5, 140, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert spacebar L
|
||||
{ 0, 6, 7, 141, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert spacebar R
|
||||
}
|
||||
};
|
||||
|
||||
@@ -167,10 +194,10 @@ keyboard_keymap_overlay_values corsair_K70_CORE_TKL_layout
|
||||
| Edit Keys |
|
||||
| Zone, Row, Column, Value, Name, Alternate Name, OpCode, |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
{ 0, 0, 14, 70, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap PRSC with Mute
|
||||
{ 0, 0, 14, 70, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap PRSC with Mute
|
||||
{ 0, 0, 15, 71, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove SCLK
|
||||
{ 0, 0, 16, 72, KEY_EN_MEDIA_VOLUME_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap PSBK with Volume Potion Up
|
||||
|
||||
{ 0, 0, 16, 72, KEY_EN_MEDIA_VOLUME_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap PSBK with Volume Potion Up
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -772,6 +799,38 @@ static const corsair_v2_device k55_rgb_pro_device =
|
||||
nullptr
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------*\
|
||||
| Corsair K57 RGB (Wired) 1B1C:1B6E |
|
||||
| |
|
||||
| Zone "Keyboard" |
|
||||
| Matrix |
|
||||
| 7 Rows, 22 Columns |
|
||||
\*-------------------------------------------------------------*/
|
||||
static const corsair_v2_zone k57_rgb_wired_zone =
|
||||
{
|
||||
ZONE_EN_KEYBOARD,
|
||||
ZONE_TYPE_MATRIX,
|
||||
7,
|
||||
22
|
||||
};
|
||||
|
||||
static const corsair_v2_device k57_rgb_wired_device =
|
||||
{
|
||||
CORSAIR_K57_RGB_WIRED_PID,
|
||||
DEVICE_TYPE_KEYBOARD,
|
||||
7,
|
||||
22,
|
||||
{
|
||||
&k57_rgb_wired_zone,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr
|
||||
},
|
||||
&corsair_K57_layout
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------*\
|
||||
| Corsair K60 RGB Pro 1B1C:1BA0 |
|
||||
| |
|
||||
@@ -1461,6 +1520,7 @@ const corsair_v2_device* corsair_v2_device_list_data[] =
|
||||
| KEYBOARDS |
|
||||
\*-----------------------------------------------------------------*/
|
||||
&k55_rgb_pro_device,
|
||||
&k57_rgb_wired_device,
|
||||
&k60_rgb_pro_device,
|
||||
&k60_rgb_pro_lp_device,
|
||||
&k60_rgb_pro_tkl_device_b,
|
||||
|
||||
@@ -69,6 +69,8 @@ typedef struct
|
||||
| Corsair V2 Protocol Keyboards |
|
||||
\*-----------------------------------------------------*/
|
||||
#define CORSAIR_K55_RGB_PRO_PID 0x1BA4
|
||||
#define CORSAIR_K57_RGB_WIRED_PID 0x1B6E
|
||||
#define CORSAIR_K57_RGB_WIRELESS_PID 0x1B62
|
||||
#define CORSAIR_K60_RGB_PRO_PID 0x1BA0
|
||||
#define CORSAIR_K60_RGB_PRO_LP_PID 0x1BAD
|
||||
#define CORSAIR_K60_RGB_PRO_TKL_B_PID 0x1BC7
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| CorsairWirelessController.cpp |
|
||||
| |
|
||||
| Driver for Corsair wireless keyboard |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 08 May 2021 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <cstring>
|
||||
#include "CorsairWirelessController.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
CorsairWirelessController::CorsairWirelessController(hid_device* dev_handle, const char* path)
|
||||
{
|
||||
dev = dev_handle;
|
||||
location = path;
|
||||
|
||||
type = DEVICE_TYPE_KEYBOARD;
|
||||
|
||||
EnterDirectMode();
|
||||
}
|
||||
|
||||
CorsairWirelessController::~CorsairWirelessController()
|
||||
{
|
||||
hid_close(dev);
|
||||
}
|
||||
|
||||
device_type CorsairWirelessController::GetDeviceType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
std::string CorsairWirelessController::GetDeviceLocation()
|
||||
{
|
||||
return("HID: " + location);
|
||||
}
|
||||
|
||||
std::string CorsairWirelessController::GetFirmwareString()
|
||||
{
|
||||
return firmware_version;
|
||||
}
|
||||
|
||||
std::string CorsairWirelessController::GetName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string CorsairWirelessController::GetSerialString()
|
||||
{
|
||||
wchar_t serial_string[128];
|
||||
int ret = hid_get_serial_number_string(dev, serial_string, 128);
|
||||
|
||||
if(ret != 0)
|
||||
{
|
||||
return("");
|
||||
}
|
||||
|
||||
return(StringUtils::wstring_to_string(serial_string));
|
||||
}
|
||||
|
||||
void CorsairWirelessController::SetLEDs(std::vector<RGBColor>colors)
|
||||
{
|
||||
unsigned char buf[3 * 137];
|
||||
|
||||
for(int color = 0; color < 137; color++)
|
||||
{
|
||||
buf[0 + color] = RGBGetRValue(colors[color]);
|
||||
buf[137 + color] = RGBGetGValue(colors[color]);
|
||||
buf[274+color] = RGBGetBValue(colors[color]);
|
||||
}
|
||||
|
||||
StartDirectMode();
|
||||
|
||||
SendDirectFrame(true, &buf[0]);
|
||||
SendDirectFrame(false, &buf[57]);
|
||||
SendDirectFrame(false, &buf[118]);
|
||||
SendDirectFrame(false, &buf[179]);
|
||||
SendDirectFrame(false, &buf[240]);
|
||||
SendDirectFrame(false, &buf[301]);
|
||||
SendDirectFrame(false, &buf[362]);
|
||||
}
|
||||
|
||||
void CorsairWirelessController::SetName(std::string device_name)
|
||||
{
|
||||
name = device_name;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------------*\
|
||||
| Private packet sending functions. |
|
||||
\*-------------------------------------------------------------------------------------------------*/
|
||||
|
||||
void CorsairWirelessController::EnterDirectMode()
|
||||
{
|
||||
unsigned char usb_buf[65];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up Submit Mouse Colors packet |
|
||||
\*-----------------------------------------------------*/
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x80;
|
||||
usb_buf[0x02] = 0x01;
|
||||
usb_buf[0x03] = 0x03;
|
||||
usb_buf[0x04] = 0x00;
|
||||
usb_buf[0x05] = 0x02;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet using feature reports, as headset stand |
|
||||
| seems to not update completely using HID writes |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev, usb_buf, 65);
|
||||
|
||||
}
|
||||
|
||||
void CorsairWirelessController::StartDirectMode()
|
||||
{
|
||||
unsigned char usb_buf[65];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up Submit Mouse Colors packet |
|
||||
\*-----------------------------------------------------*/
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x80;
|
||||
usb_buf[0x02] = 0x0D;
|
||||
usb_buf[0x03] = 0x00;
|
||||
usb_buf[0x04] = 0x01;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet using feature reports, as headset stand |
|
||||
| seems to not update completely using HID writes |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev, usb_buf, 65);
|
||||
|
||||
}
|
||||
|
||||
void CorsairWirelessController::ExitDirectMode()
|
||||
{
|
||||
unsigned char usb_buf[65];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up Submit Mouse Colors packet |
|
||||
\*-----------------------------------------------------*/
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x80;
|
||||
usb_buf[0x02] = 0x01;
|
||||
usb_buf[0x03] = 0x03;
|
||||
usb_buf[0x04] = 0x00;
|
||||
usb_buf[0x05] = 0x01;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet using feature reports, as headset stand |
|
||||
| seems to not update completely using HID writes |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev, usb_buf, 65);
|
||||
|
||||
}
|
||||
|
||||
void CorsairWirelessController::SendDirectFrame(bool first_frame, unsigned char* data)
|
||||
{
|
||||
unsigned char usb_buf[65];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up Submit Mouse Colors packet |
|
||||
\*-----------------------------------------------------*/
|
||||
usb_buf[0x00] = 0x00;
|
||||
usb_buf[0x01] = 0x80;
|
||||
usb_buf[0x02] = first_frame ? 0x06 : 0x07;
|
||||
usb_buf[0x03] = 0x00;
|
||||
|
||||
if(first_frame)
|
||||
{
|
||||
usb_buf[0x04] = 0x9B;
|
||||
usb_buf[0x05] = 0x01;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Copy in colors in <RED> <GREEN> <BLUE> order |
|
||||
\*-----------------------------------------------------*/
|
||||
if(first_frame)
|
||||
{
|
||||
memcpy(&usb_buf[0x08], data, 57);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&usb_buf[0x04], data, 61);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet using feature reports, as headset stand |
|
||||
| seems to not update completely using HID writes |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev, usb_buf, 65);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| CorsairWirelessController.h |
|
||||
| |
|
||||
| Driver for Corsair wireless keyboard |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 08 May 2021 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <hidapi.h>
|
||||
#include "RGBController.h"
|
||||
|
||||
class CorsairWirelessController
|
||||
{
|
||||
public:
|
||||
CorsairWirelessController(hid_device* dev_handle, const char* path);
|
||||
~CorsairWirelessController();
|
||||
|
||||
device_type GetDeviceType();
|
||||
std::string GetDeviceLocation();
|
||||
std::string GetFirmwareString();
|
||||
std::string GetName();
|
||||
std::string GetSerialString();
|
||||
|
||||
void SetLEDs(std::vector<RGBColor> colors);
|
||||
void SetName(std::string device_name);
|
||||
|
||||
private:
|
||||
hid_device* dev;
|
||||
|
||||
std::string firmware_version;
|
||||
std::string location;
|
||||
std::string name;
|
||||
device_type type;
|
||||
|
||||
void EnterDirectMode();
|
||||
void ExitDirectMode();
|
||||
void StartDirectMode();
|
||||
void SendDirectFrame(bool first_frame, unsigned char* data);
|
||||
};
|
||||
@@ -1,62 +0,0 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| CorsairWirelessControllerDetect.cpp |
|
||||
| |
|
||||
| Detector for Corsair wireless keyboard |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 08 May 2021 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <hidapi.h>
|
||||
#include "Detector.h"
|
||||
#include "CorsairWirelessController.h"
|
||||
#include "RGBController.h"
|
||||
#include "RGBController_CorsairWireless.h"
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Corsair vendor ID |
|
||||
\*-----------------------------------------------------*/
|
||||
#define CORSAIR_VID 0x1B1C
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Keyboard product IDs |
|
||||
\*-----------------------------------------------------*/
|
||||
#define CORSAIR_K57_RGB_WIRED_PID 0x1B6E
|
||||
#define CORSAIR_K57_RGB_WIRELESS_PID 0x1B62
|
||||
|
||||
/******************************************************************************************\
|
||||
* *
|
||||
* DetectCorsairWirelessControllers *
|
||||
* *
|
||||
* Tests the USB address to see if a Corsair RGB Keyboard controller exists there. *
|
||||
* *
|
||||
\******************************************************************************************/
|
||||
|
||||
void DetectCorsairWirelessControllers(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(dev)
|
||||
{
|
||||
CorsairWirelessController* controller = new CorsairWirelessController(dev, info->path);
|
||||
controller->SetName(name);
|
||||
|
||||
if(controller->GetDeviceType() != DEVICE_TYPE_UNKNOWN)
|
||||
{
|
||||
RGBController_CorsairWireless* rgb_controller = new RGBController_CorsairWireless(controller);
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete controller;
|
||||
}
|
||||
}
|
||||
} /* DetectCorsairWirelessControllers() */
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------*\
|
||||
| Keyboards |
|
||||
\*-----------------------------------------------------------------------------------------------------*/
|
||||
REGISTER_HID_DETECTOR_IPU("Corsair K57 RGB (Wired)", DetectCorsairWirelessControllers, CORSAIR_VID, CORSAIR_K57_RGB_WIRED_PID, 1, 0xFF42, 1);
|
||||
//REGISTER_HID_DETECTOR_IPU("Corsair K57 RGB (Wireless)", DetectCorsairWirelessControllers, CORSAIR_VID, CORSAIR_K57_RGB_WIRELESS_PID, 1, 0xFF42, 1);
|
||||
@@ -1,342 +0,0 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_CorsairWireless.cpp |
|
||||
| |
|
||||
| RGBController for Corsair wireless keyboard |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 08 May 2021 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "RGBControllerKeyNames.h"
|
||||
#include "RGBController_CorsairWireless.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
//0xFFFFFFFF indicates an unused entry in matrix
|
||||
#define NA 0xFFFFFFFF
|
||||
|
||||
static unsigned int matrix_map[7][24] =
|
||||
{ { NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 0, 1, NA, NA, NA },
|
||||
{ 131, 41, NA, 58, 59, 60, 61, NA, 62, 63, 64, 65, NA, 66, 67, 68, 69, 70, 71, 72, NA, NA, NA, NA },
|
||||
{ 132, 53, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, NA, 45, 46, 42, NA, 73, 74, 75, 83, 84, 85, 86 },
|
||||
{ 133, 43, NA, 20, 26, 8, 21, NA, 23, 28, 24, 12, 18, 19, 47, 48, 49, 76, 77, 78, 95, 96, 97, 87 },
|
||||
{ 134, 57, NA, 4, 22, 7, 9, NA, 10, 11, 13, 14, 15, 51, 52, NA, 40, NA, NA, NA, 92, 93, 94, NA },
|
||||
{ 135, 106, NA, 29, 27, 6, 25, NA, 5, NA, 17, 16, 54, 55, 56, 110, NA, NA, 82, NA, 89, 90, 91, 88 },
|
||||
{ 136, 105, 108, 107, NA, NA, NA, NA, 44, NA, NA, NA, NA, 111, 122, 101, 109, 80, 81, 79, 98, NA, 99, NA } };
|
||||
|
||||
static const char* zone_names[] =
|
||||
{
|
||||
ZONE_EN_KEYBOARD,
|
||||
};
|
||||
|
||||
static const unsigned int zone_sizes[] =
|
||||
{
|
||||
137
|
||||
};
|
||||
|
||||
static const zone_type zone_types[] =
|
||||
{
|
||||
ZONE_TYPE_MATRIX,
|
||||
};
|
||||
|
||||
static const char* led_names[] =
|
||||
{
|
||||
"Power/Wireless Indicator",
|
||||
"Lock/Macro Indicator",
|
||||
"N/A",
|
||||
"N/A",
|
||||
KEY_EN_A,
|
||||
KEY_EN_B,
|
||||
KEY_EN_C,
|
||||
KEY_EN_D,
|
||||
KEY_EN_E,
|
||||
KEY_EN_F,
|
||||
KEY_EN_G,
|
||||
KEY_EN_H,
|
||||
KEY_EN_I,
|
||||
KEY_EN_J,
|
||||
KEY_EN_K,
|
||||
KEY_EN_L,
|
||||
KEY_EN_M,
|
||||
KEY_EN_N,
|
||||
KEY_EN_O,
|
||||
KEY_EN_P,
|
||||
KEY_EN_Q,
|
||||
KEY_EN_R,
|
||||
KEY_EN_S,
|
||||
KEY_EN_T,
|
||||
KEY_EN_U,
|
||||
KEY_EN_V,
|
||||
KEY_EN_W,
|
||||
KEY_EN_X,
|
||||
KEY_EN_Y,
|
||||
KEY_EN_Z,
|
||||
KEY_EN_1,
|
||||
KEY_EN_2,
|
||||
KEY_EN_3,
|
||||
KEY_EN_4,
|
||||
KEY_EN_5,
|
||||
KEY_EN_6,
|
||||
KEY_EN_7,
|
||||
KEY_EN_8,
|
||||
KEY_EN_9,
|
||||
KEY_EN_0,
|
||||
KEY_EN_ANSI_ENTER,
|
||||
KEY_EN_ESCAPE,
|
||||
KEY_EN_BACKSPACE,
|
||||
KEY_EN_TAB,
|
||||
KEY_EN_SPACE,
|
||||
KEY_EN_MINUS,
|
||||
KEY_EN_EQUALS,
|
||||
KEY_EN_LEFT_BRACKET,
|
||||
KEY_EN_RIGHT_BRACKET,
|
||||
KEY_EN_ANSI_BACK_SLASH,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_SEMICOLON,
|
||||
KEY_EN_QUOTE,
|
||||
KEY_EN_BACK_TICK,
|
||||
KEY_EN_COMMA,
|
||||
KEY_EN_PERIOD,
|
||||
KEY_EN_FORWARD_SLASH,
|
||||
KEY_EN_CAPS_LOCK,
|
||||
KEY_EN_F1,
|
||||
KEY_EN_F2,
|
||||
KEY_EN_F3,
|
||||
KEY_EN_F4,
|
||||
KEY_EN_F5,
|
||||
KEY_EN_F6,
|
||||
KEY_EN_F7,
|
||||
KEY_EN_F8,
|
||||
KEY_EN_F9,
|
||||
KEY_EN_F10,
|
||||
KEY_EN_F11,
|
||||
KEY_EN_F12,
|
||||
KEY_EN_PRINT_SCREEN,
|
||||
KEY_EN_SCROLL_LOCK,
|
||||
KEY_EN_PAUSE_BREAK,
|
||||
KEY_EN_INSERT,
|
||||
KEY_EN_HOME,
|
||||
KEY_EN_PAGE_UP,
|
||||
KEY_EN_DELETE,
|
||||
KEY_EN_END,
|
||||
KEY_EN_PAGE_DOWN,
|
||||
KEY_EN_RIGHT_ARROW,
|
||||
KEY_EN_LEFT_ARROW,
|
||||
KEY_EN_DOWN_ARROW,
|
||||
KEY_EN_UP_ARROW,
|
||||
KEY_EN_NUMPAD_LOCK,
|
||||
KEY_EN_NUMPAD_DIVIDE,
|
||||
KEY_EN_NUMPAD_TIMES,
|
||||
KEY_EN_NUMPAD_MINUS,
|
||||
KEY_EN_NUMPAD_PLUS,
|
||||
KEY_EN_NUMPAD_ENTER,
|
||||
KEY_EN_NUMPAD_1,
|
||||
KEY_EN_NUMPAD_2,
|
||||
KEY_EN_NUMPAD_3,
|
||||
KEY_EN_NUMPAD_4,
|
||||
KEY_EN_NUMPAD_5,
|
||||
KEY_EN_NUMPAD_6,
|
||||
KEY_EN_NUMPAD_7,
|
||||
KEY_EN_NUMPAD_8,
|
||||
KEY_EN_NUMPAD_9,
|
||||
KEY_EN_NUMPAD_0,
|
||||
KEY_EN_NUMPAD_PERIOD,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_MENU,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_LEFT_CONTROL,
|
||||
KEY_EN_LEFT_SHIFT,
|
||||
KEY_EN_LEFT_ALT,
|
||||
KEY_EN_LEFT_WINDOWS,
|
||||
KEY_EN_RIGHT_CONTROL,
|
||||
KEY_EN_RIGHT_SHIFT,
|
||||
KEY_EN_RIGHT_ALT,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_RIGHT_FUNCTION,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_UNUSED,
|
||||
"Key: G1",
|
||||
"Key: G2",
|
||||
"Key: G3",
|
||||
"Key: G4",
|
||||
"Key: G5",
|
||||
"Key: G6",
|
||||
};
|
||||
|
||||
/**------------------------------------------------------------------*\
|
||||
@name Corsair Wireless Peripheral
|
||||
@category Keyboard
|
||||
@type USB
|
||||
@save :x:
|
||||
@direct :white_check_mark:
|
||||
@effects :x:
|
||||
@detectors DetectCorsairWirelessControllers
|
||||
@comment
|
||||
\*-------------------------------------------------------------------*/
|
||||
|
||||
RGBController_CorsairWireless::RGBController_CorsairWireless(CorsairWirelessController* controller_ptr)
|
||||
{
|
||||
controller = controller_ptr;
|
||||
|
||||
name = controller->GetName();
|
||||
vendor = "Corsair";
|
||||
description = "Corsair RGB Peripheral Device";
|
||||
type = controller->GetDeviceType();
|
||||
version = controller->GetFirmwareString();
|
||||
location = controller->GetDeviceLocation();
|
||||
serial = controller->GetSerialString();
|
||||
|
||||
mode Direct;
|
||||
Direct.name = "Direct";
|
||||
Direct.value = 0;
|
||||
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
|
||||
Direct.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Direct);
|
||||
|
||||
SetupZones();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| The Corsair Lighting Node Pro requires a packet within|
|
||||
| 20 seconds of sending the lighting change in order |
|
||||
| to not revert back into rainbow mode. Start a thread |
|
||||
| to continuously send a keepalive packet every 5s |
|
||||
\*-----------------------------------------------------*/
|
||||
keepalive_thread_run = true;
|
||||
keepalive_thread = new std::thread(&RGBController_CorsairWireless::KeepaliveThread, this);
|
||||
}
|
||||
|
||||
RGBController_CorsairWireless::~RGBController_CorsairWireless()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Close keepalive thread |
|
||||
\*-----------------------------------------------------*/
|
||||
keepalive_thread_run = false;
|
||||
keepalive_thread->join();
|
||||
delete keepalive_thread;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Delete the matrix map |
|
||||
\*---------------------------------------------------------*/
|
||||
for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++)
|
||||
{
|
||||
if(zones[zone_index].matrix_map != NULL)
|
||||
{
|
||||
delete zones[zone_index].matrix_map;
|
||||
}
|
||||
}
|
||||
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void RGBController_CorsairWireless::SetupZones()
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Determine number of zones |
|
||||
| For now, keyboard has 2 zones and mousemat has 1 |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned int num_zones = 1;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Set up zones |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned int total_led_count = 0;
|
||||
for(unsigned int zone_idx = 0; zone_idx < num_zones; zone_idx++)
|
||||
{
|
||||
zone new_zone;
|
||||
|
||||
new_zone.name = zone_names[zone_idx];
|
||||
new_zone.type = zone_types[zone_idx];
|
||||
new_zone.leds_min = zone_sizes[zone_idx];
|
||||
new_zone.leds_max = zone_sizes[zone_idx];
|
||||
new_zone.leds_count = zone_sizes[zone_idx];
|
||||
|
||||
if(zone_types[zone_idx] == ZONE_TYPE_MATRIX)
|
||||
{
|
||||
new_zone.matrix_map = new matrix_map_type;
|
||||
new_zone.matrix_map->height = 7;
|
||||
new_zone.matrix_map->width = 24;
|
||||
new_zone.matrix_map->map = (unsigned int *)&matrix_map;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_zone.matrix_map = NULL;
|
||||
}
|
||||
|
||||
zones.push_back(new_zone);
|
||||
total_led_count += new_zone.leds_count;
|
||||
}
|
||||
|
||||
for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++)
|
||||
{
|
||||
led new_led;
|
||||
|
||||
new_led.name = led_names[led_idx];
|
||||
|
||||
leds.push_back(new_led);
|
||||
}
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
void RGBController_CorsairWireless::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| This device does not support resizing zones |
|
||||
\*---------------------------------------------------------*/
|
||||
}
|
||||
|
||||
void RGBController_CorsairWireless::DeviceUpdateLEDs()
|
||||
{
|
||||
last_update_time = std::chrono::steady_clock::now();
|
||||
|
||||
controller->SetLEDs(colors);
|
||||
}
|
||||
|
||||
void RGBController_CorsairWireless::UpdateZoneLEDs(int /*zone*/)
|
||||
{
|
||||
controller->SetLEDs(colors);
|
||||
}
|
||||
|
||||
void RGBController_CorsairWireless::UpdateSingleLED(int /*led*/)
|
||||
{
|
||||
controller->SetLEDs(colors);
|
||||
}
|
||||
|
||||
void RGBController_CorsairWireless::DeviceUpdateMode()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RGBController_CorsairWireless::KeepaliveThread()
|
||||
{
|
||||
while(keepalive_thread_run.load())
|
||||
{
|
||||
if(active_mode == 0)
|
||||
{
|
||||
if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(5000))
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(1000ms);
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,14 @@ void CrucialController::CrucialRegisterWriteBlock(crucial_register reg, unsigned
|
||||
bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF));
|
||||
|
||||
//Write Crucial block data
|
||||
bus->i2c_smbus_write_block_data(dev, 0x03, sz, data);
|
||||
if(bus->i2c_smbus_write_block_data(dev, 0x03, sz, data) == -1)
|
||||
{
|
||||
//Fall back to individual byte operations if the block operation fails
|
||||
for(unsigned int block_byte = 0; block_byte < sz; block_byte++)
|
||||
{
|
||||
bus->i2c_smbus_write_byte_data(dev, 0x01, data[block_byte]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CrucialController::SendEffectColor
|
||||
|
||||
@@ -27,12 +27,6 @@ using namespace std::chrono_literals;
|
||||
|
||||
static const unsigned char crucial_addresses[] =
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| These addresses have been disabled due to conflict |
|
||||
| with ASUS Aura DRAM. Since the detection scheme is |
|
||||
| the same, Aura RAM will be detected as Crucial. |
|
||||
| We need to improve the Crucial detection scheme. |
|
||||
\*-----------------------------------------------------*/
|
||||
0x39,
|
||||
0x3A,
|
||||
0x3B,
|
||||
@@ -81,7 +75,7 @@ unsigned char CrucialRegisterRead(i2c_smbus_interface* bus, crucial_dev_id dev,
|
||||
* TestForCrucialController *
|
||||
* *
|
||||
* Tests the given address to see if an Crucial controller exists there. First does a*
|
||||
* quick write to test for a response, and if so does a simple read at 0xA0 to test *
|
||||
* byte read to test for a response, and if so does a simple read at 0xA0 to test *
|
||||
* for incrementing values 0...F which was observed at this location during data dump *
|
||||
* *
|
||||
\******************************************************************************************/
|
||||
@@ -90,19 +84,19 @@ bool TestForCrucialController(i2c_smbus_interface* bus, unsigned char address)
|
||||
{
|
||||
bool pass = false;
|
||||
|
||||
int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE);
|
||||
int res = bus->i2c_smbus_read_byte(address);
|
||||
|
||||
if (res >= 0)
|
||||
if(res >= 0)
|
||||
{
|
||||
pass = true;
|
||||
|
||||
LOG_DEBUG("[%s] Detected an I2C device at address %02X", CRUCIAL_CONTROLLER_NAME, address);
|
||||
|
||||
for (int i = 0xA0; i < 0xB0; i++)
|
||||
for(int i = 0xA0; i < 0xB0; i++)
|
||||
{
|
||||
res = bus->i2c_smbus_read_byte_data(address, i);
|
||||
|
||||
if (res != (i - 0xA0))
|
||||
if(res != (i - 0xA0))
|
||||
{
|
||||
LOG_VERBOSE("[%s] Detection failed testing register %02X. Expected %02X, got %02X.", CRUCIAL_CONTROLLER_NAME, i, (i - 0xA0), res);
|
||||
|
||||
@@ -178,7 +172,7 @@ void DetectCrucialControllers(std::vector<i2c_smbus_interface*> &busses)
|
||||
{
|
||||
for(unsigned int slot = 0; slot < 4; slot++)
|
||||
{
|
||||
int res = busses[bus]->i2c_smbus_write_quick(0x27, I2C_SMBUS_WRITE);
|
||||
int res = busses[bus]->i2c_smbus_read_byte(0x27);
|
||||
|
||||
if(res < 0)
|
||||
{
|
||||
@@ -192,7 +186,7 @@ void DetectCrucialControllers(std::vector<i2c_smbus_interface*> &busses)
|
||||
|
||||
if(address_list_idx < CRUCIAL_ADDRESS_COUNT)
|
||||
{
|
||||
res = busses[bus]->i2c_smbus_write_quick(crucial_addresses[address_list_idx], I2C_SMBUS_WRITE);
|
||||
res = busses[bus]->i2c_smbus_read_byte(crucial_addresses[address_list_idx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -112,7 +112,7 @@ static void ENERegisterWrite(i2c_smbus_interface* bus, ene_dev_id dev, ene_regis
|
||||
* TestForENESMBusController *
|
||||
* *
|
||||
* Tests the given address to see if an ENE controller exists there. First does a *
|
||||
* quick write to test for a response, and if so does a simple read at 0xA0 to test *
|
||||
* byte read to test for a response, and if so does a simple read at 0xA0 to test *
|
||||
* for incrementing values 0...F which was observed at this location during data dump *
|
||||
* *
|
||||
* Also tests for the string "Micron" in the ENE register space. Crucial (Micron) *
|
||||
@@ -202,7 +202,7 @@ void DetectENESMBusDRAMControllers(std::vector<i2c_smbus_interface*> &busses)
|
||||
|
||||
for (unsigned int slot = 0; slot < 8; slot++)
|
||||
{
|
||||
int res = busses[bus]->i2c_smbus_write_quick(0x77, I2C_SMBUS_WRITE);
|
||||
int res = busses[bus]->i2c_smbus_read_byte(0x77);
|
||||
|
||||
if(res < 0)
|
||||
{
|
||||
@@ -219,7 +219,7 @@ void DetectENESMBusDRAMControllers(std::vector<i2c_smbus_interface*> &busses)
|
||||
{
|
||||
LOG_DEBUG("[ENE SMBus DRAM] Testing address %02X to see if there is a device there", ene_ram_addresses[address_list_idx]);
|
||||
|
||||
res = busses[bus]->i2c_smbus_write_quick(ene_ram_addresses[address_list_idx], I2C_SMBUS_WRITE);
|
||||
res = busses[bus]->i2c_smbus_read_byte(ene_ram_addresses[address_list_idx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -350,6 +350,7 @@ REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 Ti Gaming OC",
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Ti OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060TI_O8G_OC, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 Ti OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3060TI_O8G_OC_V2, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Ti V2 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060TI_O8G_V2_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Ti V2 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060TI_O8G_V2_2_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_OC, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_O8G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_O8G_WHITE, 0x67);
|
||||
@@ -457,8 +458,11 @@ REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White",
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_O24G_GAMING_WHITE, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_O24G_GAMING_WHITE_2, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG MATRIX PLATINUM GeForce RTX 4090", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_MATRIX_PLATINUM_RTX_4090_24G, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070_O12G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5060 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5060_O8G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070_O12G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming BTF White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING_BTF_WHITE, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING_WHITE, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 5070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_5070TI_O16G_GAMING_OC, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5080 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5080_O16G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5090_O32G_GAMING, 0x67);
|
||||
@@ -466,7 +470,9 @@ REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5090 Gaming",
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_O16G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 OC WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_O16G_GAMING_WHITE, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING_WHITE, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_2, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC BTF", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_BTF, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_WHITE, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL LC GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_LC_RTX_5090_O32G_GAMING, 0x67);
|
||||
@@ -497,6 +503,7 @@ REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7700 XT Gaming OC",
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7800 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7800XT_O16G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7800 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7800XT_O16G_GAMING_0606, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7800 XT Gaming White OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7800XT_O16G_WHITE_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7900 GRE Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7900GRE_O16G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7900 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7900XT_020G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7900 XTX Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7900XTX_O24G_GAMING, 0x67);
|
||||
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 9070 Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_9070_016G_GAMING, 0x67);
|
||||
|
||||
@@ -61,5 +61,12 @@ void ENESMBusInterface_i2c_smbus::ENERegisterWriteBlock(ene_dev_id dev, ene_regi
|
||||
bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF));
|
||||
|
||||
//Write ENE block data
|
||||
bus->i2c_smbus_write_block_data(dev, 0x03, sz, data);
|
||||
if(bus->i2c_smbus_write_block_data(dev, 0x03, sz, data) == -1)
|
||||
{
|
||||
//Fall back to individual byte operations if the block operation fails
|
||||
for(unsigned int block_byte = 0; block_byte < sz; block_byte++)
|
||||
{
|
||||
bus->i2c_smbus_write_byte_data(dev, 0x01, data[block_byte]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ FnaticStreakController::~FnaticStreakController()
|
||||
|
||||
std::string FnaticStreakController::GetDeviceLocation()
|
||||
{
|
||||
return("HID " + location);
|
||||
return("HID: " + location);
|
||||
}
|
||||
|
||||
std::string FnaticStreakController::GetNameString()
|
||||
@@ -230,16 +230,41 @@ void FnaticStreakController::SetLEDsDirect(std::vector<led> leds, std::vector<RG
|
||||
|
||||
void FnaticStreakController::SendRGBToDevice()
|
||||
{
|
||||
unsigned int total_leds = GetLEDCount();
|
||||
unsigned int data_size = total_leds * 3;
|
||||
if(keyboard_type == FNATIC_STREAK_TYPE_65)
|
||||
{
|
||||
unsigned char data[64];
|
||||
memset(data, 0x00, sizeof(data));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| For direct/software control, use command 0x0f |
|
||||
| This bypasses the profile and allows immediate update |
|
||||
| The 0x03 subcommand indicates per-key color data |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned char prefix[] = { 0x0f, 0x03 };
|
||||
SendRequest(prefix, sizeof(prefix), color_buf, data_size);
|
||||
data[0] = 0x0F;
|
||||
data[1] = 0x15;
|
||||
|
||||
data[7] = 0x0F;
|
||||
data[8] = 0x03;
|
||||
|
||||
for(unsigned int i = 9; i < 25; i++)
|
||||
{
|
||||
data[i] = 0xFF;
|
||||
}
|
||||
|
||||
data[25] = color_buf[0];
|
||||
data[26] = color_buf[1];
|
||||
data[27] = color_buf[2];
|
||||
|
||||
hid_write(dev, data, sizeof(data));
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int total_leds = GetLEDCount();
|
||||
unsigned int data_size = total_leds * 3;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| For direct/software control, use command 0x0f |
|
||||
| This bypasses the profile and allows immediate update |
|
||||
| The 0x03 subcommand indicates per-key color data |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned char prefix[] = { 0x0f, 0x03 };
|
||||
SendRequest(prefix, sizeof(prefix), color_buf, data_size);
|
||||
}
|
||||
}
|
||||
|
||||
void FnaticStreakController::SetPulse(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed)
|
||||
|
||||
@@ -51,7 +51,8 @@
|
||||
enum FnaticStreakType
|
||||
{
|
||||
FNATIC_STREAK_TYPE_FULL,
|
||||
FNATIC_STREAK_TYPE_MINI
|
||||
FNATIC_STREAK_TYPE_MINI,
|
||||
FNATIC_STREAK_TYPE_65
|
||||
};
|
||||
|
||||
class FnaticStreakController
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#define FNATIC_STREAK_PID 0x0101
|
||||
#define FNATIC_MINISTREAK_PID 0x0102
|
||||
#define FNATIC_STREAK65_PID 0x0105
|
||||
|
||||
void DetectFnaticStreakKeyboard(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
@@ -39,6 +40,10 @@ void DetectFnaticStreakKeyboard(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
kb_type = FNATIC_STREAK_TYPE_MINI;
|
||||
}
|
||||
else if(info->product_id == FNATIC_STREAK65_PID)
|
||||
{
|
||||
kb_type = FNATIC_STREAK_TYPE_65;
|
||||
}
|
||||
else
|
||||
{
|
||||
kb_type = FNATIC_STREAK_TYPE_FULL;
|
||||
@@ -53,3 +58,4 @@ void DetectFnaticStreakKeyboard(hid_device_info* info, const std::string& name)
|
||||
|
||||
REGISTER_HID_DETECTOR_I("Fnatic Streak", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_STREAK_PID, 1);
|
||||
REGISTER_HID_DETECTOR_I("Fnatic miniStreak", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_MINISTREAK_PID, 1);
|
||||
REGISTER_HID_DETECTOR_I("Fnatic Streak65", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_STREAK65_PID, 1);
|
||||
|
||||
@@ -37,19 +37,22 @@ bool TestForGalaxGPUController(i2c_smbus_interface* bus, unsigned char address)
|
||||
\*-----------------------------------------------------------------*/
|
||||
case 0x32:
|
||||
res = bus->i2c_smbus_read_byte_data(address, 0x00);
|
||||
if(res == 0x27 || res == 0x26) {
|
||||
if(res == 0x27 || res == 0x26)
|
||||
{
|
||||
res = bus->i2c_smbus_read_byte_data(address, 0x01);
|
||||
if (res == 0x10 || res == 0x20)
|
||||
if(res == 0x10 || res == 0x20)
|
||||
{
|
||||
pass = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/*-----------------------------------------------------------------*\
|
||||
| V1 Controller - RTX 3080 |
|
||||
| V1 Controller |
|
||||
\*-----------------------------------------------------------------*/
|
||||
case 0x23:
|
||||
res = bus->i2c_smbus_read_byte_data(address, 0x00);
|
||||
if(res == 0x30)
|
||||
if(res == 0x27 || res == 0x30)
|
||||
{
|
||||
pass = true;
|
||||
}
|
||||
@@ -60,8 +63,10 @@ bool TestForGalaxGPUController(i2c_smbus_interface* bus, unsigned char address)
|
||||
\*-----------------------------------------------------------------*/
|
||||
case 0x51:
|
||||
res = bus->i2c_smbus_read_byte_data(address, 0x00);
|
||||
if (res == 0x80)
|
||||
if(res == 0x80)
|
||||
{
|
||||
pass = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| GigabyteCastor3Controller.cpp |
|
||||
| |
|
||||
| Driver for Gigabyte Aorus Waterforce X II 360 AIO |
|
||||
| (Castor3 USB HID controller) |
|
||||
| |
|
||||
| RGB ring control only — LCD not implemented |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <cstring>
|
||||
#include "GigabyteCastor3Controller.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
GigabyteCastor3Controller::GigabyteCastor3Controller(hid_device* dev_handle, const char* path)
|
||||
{
|
||||
dev = dev_handle;
|
||||
location = path;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Query firmware version via de 00 |
|
||||
| Response: de [version_byte] |
|
||||
| Observed: de 02 -> version 2 |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned char de_payload[] = { 0xDE, 0x00 };
|
||||
SendPacket(de_payload, 2);
|
||||
|
||||
unsigned char response[CASTOR3_IN_SIZE];
|
||||
int bytes_read = hid_read_timeout(dev, response, CASTOR3_IN_SIZE, CASTOR3_IN_TIMEOUT_MS);
|
||||
|
||||
if(bytes_read > 1)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Strip report ID 0x99 if present on IN packets |
|
||||
\*-----------------------------------------------------*/
|
||||
int offset = 0;
|
||||
if(response[0] == CASTOR3_REPORT_ID)
|
||||
{
|
||||
offset = 1;
|
||||
}
|
||||
|
||||
if(response[offset] == 0xDE && bytes_read > offset + 1)
|
||||
{
|
||||
firmware_version = std::to_string(response[offset + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GigabyteCastor3Controller::~GigabyteCastor3Controller()
|
||||
{
|
||||
hid_close(dev);
|
||||
}
|
||||
|
||||
std::string GigabyteCastor3Controller::GetDeviceLocation()
|
||||
{
|
||||
return("HID: " + location);
|
||||
}
|
||||
|
||||
std::string GigabyteCastor3Controller::GetSerialString()
|
||||
{
|
||||
wchar_t serial_string[128];
|
||||
int ret = hid_get_serial_number_string(dev, serial_string, 128);
|
||||
|
||||
if(ret != 0)
|
||||
{
|
||||
return("");
|
||||
}
|
||||
|
||||
return(StringUtils::wstring_to_string(serial_string));
|
||||
}
|
||||
|
||||
std::string GigabyteCastor3Controller::GetFirmwareVersion()
|
||||
{
|
||||
return firmware_version;
|
||||
}
|
||||
|
||||
void GigabyteCastor3Controller::SetEffect
|
||||
(
|
||||
unsigned char style,
|
||||
unsigned char speed,
|
||||
unsigned char brightness,
|
||||
unsigned char b4,
|
||||
unsigned char b5,
|
||||
castor3_color_type color_type,
|
||||
std::vector<RGBColor> colors
|
||||
)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Wire protocol for LED effect apply |
|
||||
| (from castor3.py led_set_effect, confirmed from |
|
||||
| led_profile.pcapng + .cled.dat cross-reference) |
|
||||
| |
|
||||
| Step 1: c9 [Style] [Speed*20] [Bright] [b4] [b5] |
|
||||
| Step 2: cd [R] [G] [B] (single-color only) |
|
||||
| -OR- b0..b3 palette registers (palette effects) |
|
||||
| Step 3: b6 (commit) |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Step 1: c9 — effect select + parameters |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned char c9_payload[] =
|
||||
{
|
||||
0xC9,
|
||||
style,
|
||||
(unsigned char)(speed * 20),
|
||||
brightness,
|
||||
b4,
|
||||
b5
|
||||
};
|
||||
SendPacket(c9_payload, sizeof(c9_payload));
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Step 2a: cd — primary color for single-color effects |
|
||||
| Effects: static, pulse, flash, dflash, gradient, off |
|
||||
\*---------------------------------------------------------*/
|
||||
if(color_type == CASTOR3_COLORS_SINGLE)
|
||||
{
|
||||
unsigned char r = 0xFF, g = 0x66, b = 0x00;
|
||||
|
||||
if(colors.size() > 0)
|
||||
{
|
||||
r = RGBGetRValue(colors[0]);
|
||||
g = RGBGetGValue(colors[0]);
|
||||
b = RGBGetBValue(colors[0]);
|
||||
}
|
||||
|
||||
unsigned char cd_payload[] = { 0xCD, r, g, b };
|
||||
SendPacket(cd_payload, sizeof(cd_payload));
|
||||
}
|
||||
/*---------------------------------------------------------*\
|
||||
| Step 2b: b0..b3 — palette registers for multi-color |
|
||||
| Each register carries 2 colors (6 bytes RGB + 1 pad): |
|
||||
| bN [Style] [R1 G1 B1] [R2 G2 B2] 0x00 |
|
||||
| b0=colors 1+2, b1=colors 3+4, b2=colors 5+6, b3=7+8 |
|
||||
| |
|
||||
| Effects: colorshift(8), tricolor(3), spin(3), switch(2) |
|
||||
\*---------------------------------------------------------*/
|
||||
else if(color_type == CASTOR3_COLORS_PALETTE)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Build palette: pad to 8 colors by repeating last |
|
||||
\*-----------------------------------------------------*/
|
||||
RGBColor palette[8];
|
||||
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
if(i < (int)colors.size())
|
||||
{
|
||||
palette[i] = colors[i];
|
||||
}
|
||||
else if(colors.size() > 0)
|
||||
{
|
||||
palette[i] = colors[colors.size() - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
palette[i] = ToRGBColor(0xFF, 0x00, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char regs[] = { 0xB0, 0xB1, 0xB2, 0xB3 };
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
RGBColor c1 = palette[i * 2];
|
||||
RGBColor c2 = palette[i * 2 + 1];
|
||||
|
||||
unsigned char bN_payload[] =
|
||||
{
|
||||
regs[i],
|
||||
style,
|
||||
(unsigned char)RGBGetRValue(c1), (unsigned char)RGBGetGValue(c1), (unsigned char)RGBGetBValue(c1),
|
||||
(unsigned char)RGBGetRValue(c2), (unsigned char)RGBGetGValue(c2), (unsigned char)RGBGetBValue(c2),
|
||||
0x00
|
||||
};
|
||||
SendPacket(bN_payload, sizeof(bN_payload));
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Step 3: b6 — commit/apply |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned char b6_payload[] = { 0xB6 };
|
||||
SendPacket(b6_payload, sizeof(b6_payload));
|
||||
}
|
||||
|
||||
void GigabyteCastor3Controller::SetOff()
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Off = Style 0x01 (Static) with Speed=0 |
|
||||
| From castor3.py: off uses style 0x01, speed_wire=0 |
|
||||
\*---------------------------------------------------------*/
|
||||
std::vector<RGBColor> black;
|
||||
black.push_back(ToRGBColor(0x00, 0x00, 0x00));
|
||||
|
||||
unsigned char c9_payload[] =
|
||||
{
|
||||
0xC9,
|
||||
CASTOR3_STYLE_STATIC,
|
||||
0x00, /* speed_wire = 0 for off */
|
||||
CASTOR3_BRIGHTNESS_DEFAULT,
|
||||
0x02, /* b4 */
|
||||
0x01 /* b5 */
|
||||
};
|
||||
SendPacket(c9_payload, sizeof(c9_payload));
|
||||
|
||||
unsigned char cd_payload[] = { 0xCD, 0x00, 0x00, 0x00 };
|
||||
SendPacket(cd_payload, sizeof(cd_payload));
|
||||
|
||||
unsigned char b6_payload[] = { 0xB6 };
|
||||
SendPacket(b6_payload, sizeof(b6_payload));
|
||||
}
|
||||
|
||||
void GigabyteCastor3Controller::SendPacket(const unsigned char* payload, unsigned int payload_len)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Castor3 HID OUT transport: |
|
||||
| - Byte 0: Report ID (0x99) |
|
||||
| - Bytes 1..6143: payload, zero-padded |
|
||||
| Total write size: 6144 bytes |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned char buf[CASTOR3_OUT_TOTAL];
|
||||
memset(buf, 0x00, CASTOR3_OUT_TOTAL);
|
||||
|
||||
buf[0] = CASTOR3_REPORT_ID;
|
||||
|
||||
if(payload_len > CASTOR3_OUT_PAYLOAD)
|
||||
{
|
||||
payload_len = CASTOR3_OUT_PAYLOAD;
|
||||
}
|
||||
|
||||
memcpy(&buf[1], payload, payload_len);
|
||||
|
||||
hid_write(dev, buf, CASTOR3_OUT_TOTAL);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| GigabyteCastor3Controller.h |
|
||||
| |
|
||||
| Driver for Gigabyte Aorus Waterforce X II 360 AIO |
|
||||
| (Castor3 USB HID controller) |
|
||||
| |
|
||||
| RGB ring control only — LCD not implemented |
|
||||
| |
|
||||
| Protocol reference: castor3.py reverse engineering |
|
||||
| VID=0x0414 PID=0x7A5E Report ID=0x99 |
|
||||
| OUT size: 6144 (report ID + 6143 payload) |
|
||||
| IN size: 255 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <hidapi.h>
|
||||
#include "RGBController.h"
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Castor3 USB IDs |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#define CASTOR3_VID 0x0414
|
||||
#define CASTOR3_PID 0x7A5E
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Castor3 HID transport |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#define CASTOR3_REPORT_ID 0x99
|
||||
#define CASTOR3_OUT_PAYLOAD 6143 /* payload after report ID */
|
||||
#define CASTOR3_OUT_TOTAL 6144 /* report ID + payload */
|
||||
#define CASTOR3_IN_SIZE 255
|
||||
#define CASTOR3_IN_TIMEOUT_MS 2000
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| LED effect style IDs (c9 byte[1]) |
|
||||
| From castor3.py LED_EFFECTS, cross-referenced with |
|
||||
| .cled.dat profile files and led_profile.pcapng |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#define CASTOR3_STYLE_STATIC 0x01
|
||||
#define CASTOR3_STYLE_PULSE 0x02
|
||||
#define CASTOR3_STYLE_CYCLE 0x03
|
||||
#define CASTOR3_STYLE_FLASH 0x04
|
||||
#define CASTOR3_STYLE_DFLASH 0x05
|
||||
#define CASTOR3_STYLE_GRADIENT 0x06
|
||||
#define CASTOR3_STYLE_COLORSHIFT 0x07
|
||||
#define CASTOR3_STYLE_WAVE 0x08
|
||||
#define CASTOR3_STYLE_RAINBOW 0x0A
|
||||
#define CASTOR3_STYLE_TRICOLOR 0x0B
|
||||
#define CASTOR3_STYLE_SPIN 0x0C
|
||||
#define CASTOR3_STYLE_SWITCH 0x0D
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| LED speed range: 1-5 (wire value = speed * 20) |
|
||||
| LED brightness range: 1-10 (direct on wire) |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#define CASTOR3_SPEED_MIN 1
|
||||
#define CASTOR3_SPEED_MAX 5
|
||||
#define CASTOR3_SPEED_DEFAULT 5
|
||||
#define CASTOR3_BRIGHTNESS_MIN 1
|
||||
#define CASTOR3_BRIGHTNESS_MAX 10
|
||||
#define CASTOR3_BRIGHTNESS_DEFAULT 10
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Color type for effects |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
enum castor3_color_type
|
||||
{
|
||||
CASTOR3_COLORS_NONE = 0, /* cycle, wave, rainbow */
|
||||
CASTOR3_COLORS_SINGLE = 1, /* static, pulse, flash... */
|
||||
CASTOR3_COLORS_PALETTE = 2, /* colorshift, tricolor... */
|
||||
};
|
||||
|
||||
class GigabyteCastor3Controller
|
||||
{
|
||||
public:
|
||||
GigabyteCastor3Controller(hid_device* dev_handle, const char* path);
|
||||
~GigabyteCastor3Controller();
|
||||
|
||||
std::string GetDeviceLocation();
|
||||
std::string GetSerialString();
|
||||
std::string GetFirmwareVersion();
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| LED effect application |
|
||||
| |
|
||||
| Wire sequence per effect apply: |
|
||||
| c9 [Style] [Speed*20] [Bright] [b4] [b5] |
|
||||
| cd [R] [G] [B] — single-color only |
|
||||
| b0 [Style] [R1G1B1] [R2G2B2] 00 — palette colors 1+2 |
|
||||
| b1 [Style] [R3G3B3] [R4G4B4] 00 — colors 3+4 |
|
||||
| b2 [Style] [R5G5B5] [R6G6B6] 00 — colors 5+6 |
|
||||
| b3 [Style] [R7G7B7] [R8G8B8] 00 — colors 7+8 |
|
||||
| b6 — commit/apply |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
void SetEffect(unsigned char style,
|
||||
unsigned char speed,
|
||||
unsigned char brightness,
|
||||
unsigned char b4,
|
||||
unsigned char b5,
|
||||
castor3_color_type color_type,
|
||||
std::vector<RGBColor> colors);
|
||||
|
||||
void SetOff();
|
||||
|
||||
private:
|
||||
hid_device* dev;
|
||||
std::string location;
|
||||
std::string firmware_version;
|
||||
|
||||
void SendPacket(const unsigned char* payload, unsigned int payload_len);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| GigabyteCastor3ControllerDetect.cpp |
|
||||
| |
|
||||
| Detector for Gigabyte Aorus Waterforce X II 360 AIO |
|
||||
| (Castor3 USB HID controller) |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <hidapi.h>
|
||||
#include "Detector.h"
|
||||
#include "GigabyteCastor3Controller.h"
|
||||
#include "RGBController_GigabyteCastor3.h"
|
||||
|
||||
#define GIGABYTE_CASTOR3_VID 0x0414
|
||||
#define GIGABYTE_CASTOR3_PID 0x7A5E
|
||||
|
||||
void DetectGigabyteCastor3Controllers(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(dev)
|
||||
{
|
||||
GigabyteCastor3Controller* controller = new GigabyteCastor3Controller(dev, info->path);
|
||||
RGBController_GigabyteCastor3* rgb_controller = new RGBController_GigabyteCastor3(controller);
|
||||
rgb_controller->name = name;
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_HID_DETECTOR_IPU("Gigabyte Aorus Waterforce X II 360",
|
||||
DetectGigabyteCastor3Controllers,
|
||||
GIGABYTE_CASTOR3_VID,
|
||||
GIGABYTE_CASTOR3_PID,
|
||||
0, /* interface 0 */
|
||||
0x0000, /* usage_page */
|
||||
0x0002); /* usage */
|
||||
@@ -0,0 +1,446 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_GigabyteCastor3.cpp |
|
||||
| |
|
||||
| RGBController for Gigabyte Aorus Waterforce X II 360 |
|
||||
| AIO Cooler (Castor3 controller) |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "RGBController_GigabyteCastor3.h"
|
||||
|
||||
/**------------------------------------------------------------------*\
|
||||
@name Gigabyte Aorus Waterforce X II 360
|
||||
@category Cooler
|
||||
@type USB
|
||||
@save :o:
|
||||
@direct :o:
|
||||
@effects :white_check_mark:
|
||||
@detectors DetectGigabyteCastor3Controllers
|
||||
@comment Controls the LED ring on the pump head of the
|
||||
Gigabyte Aorus Waterforce X II 360 AIO cooler.
|
||||
Uses the Castor3 USB HID controller (VID 0x0414, PID 0x7A5E).
|
||||
The LED ring and fans share the same controller — they are
|
||||
not independently addressable and follow the same effect.
|
||||
LCD display control is not implemented.
|
||||
No direct/per-LED mode available — all modes are hardware
|
||||
effects with a single color or palette.
|
||||
\*-------------------------------------------------------------------*/
|
||||
|
||||
RGBController_GigabyteCastor3::RGBController_GigabyteCastor3(GigabyteCastor3Controller* controller_ptr)
|
||||
{
|
||||
controller = controller_ptr;
|
||||
|
||||
name = "Gigabyte Aorus Waterforce X II 360";
|
||||
vendor = "Gigabyte";
|
||||
type = DEVICE_TYPE_COOLER;
|
||||
description = "Gigabyte Aorus Waterforce X II 360 AIO Cooler";
|
||||
location = controller->GetDeviceLocation();
|
||||
serial = controller->GetSerialString();
|
||||
version = controller->GetFirmwareVersion();
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| All modes from castor3.py LED_EFFECTS (12 effects + off) |
|
||||
| |
|
||||
| The Castor3 has no direct/per-LED mode. All effects are |
|
||||
| hardware-driven, either single-color, palette, or |
|
||||
| firmware-color (no user color). |
|
||||
| |
|
||||
| Speed: 1-5 mapped to MODE_FLAG_HAS_SPEED |
|
||||
| Brightness: 1-10 mapped to MODE_FLAG_HAS_BRIGHTNESS |
|
||||
| |
|
||||
| mode.value stores the style byte for the c9 command. |
|
||||
| mode.speed stores 1-5 (scaled to speed*20 on wire). |
|
||||
| mode.brightness stores 1-10 (direct on wire). |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Off |
|
||||
| Wire: Style=0x01 (Static) with speed_wire=0 |
|
||||
\*---------------------------------------------------------*/
|
||||
mode Off;
|
||||
Off.name = "Off";
|
||||
Off.value = 0xFF; /* sentinel — handled specially */
|
||||
Off.flags = 0;
|
||||
Off.color_mode = MODE_COLORS_NONE;
|
||||
modes.push_back(Off);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Static — single color, no speed |
|
||||
\*---------------------------------------------------------*/
|
||||
mode Static;
|
||||
Static.name = "Static";
|
||||
Static.value = CASTOR3_STYLE_STATIC;
|
||||
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Static.colors_min = 1;
|
||||
Static.colors_max = 1;
|
||||
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
|
||||
Static.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
Static.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
Static.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
Static.colors.resize(1);
|
||||
modes.push_back(Static);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Pulse — single color, speed + brightness |
|
||||
\*---------------------------------------------------------*/
|
||||
mode Pulse;
|
||||
Pulse.name = "Pulse";
|
||||
Pulse.value = CASTOR3_STYLE_PULSE;
|
||||
Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Pulse.speed_min = CASTOR3_SPEED_MIN;
|
||||
Pulse.speed_max = CASTOR3_SPEED_MAX;
|
||||
Pulse.speed = CASTOR3_SPEED_DEFAULT;
|
||||
Pulse.colors_min = 1;
|
||||
Pulse.colors_max = 1;
|
||||
Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC;
|
||||
Pulse.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
Pulse.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
Pulse.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
Pulse.colors.resize(1);
|
||||
modes.push_back(Pulse);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Flash — single color, speed + brightness |
|
||||
\*---------------------------------------------------------*/
|
||||
mode Flash;
|
||||
Flash.name = "Flash";
|
||||
Flash.value = CASTOR3_STYLE_FLASH;
|
||||
Flash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Flash.speed_min = CASTOR3_SPEED_MIN;
|
||||
Flash.speed_max = CASTOR3_SPEED_MAX;
|
||||
Flash.speed = CASTOR3_SPEED_DEFAULT;
|
||||
Flash.colors_min = 1;
|
||||
Flash.colors_max = 1;
|
||||
Flash.color_mode = MODE_COLORS_MODE_SPECIFIC;
|
||||
Flash.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
Flash.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
Flash.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
Flash.colors.resize(1);
|
||||
modes.push_back(Flash);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Double Flash — single color, speed + brightness |
|
||||
\*---------------------------------------------------------*/
|
||||
mode DFlash;
|
||||
DFlash.name = "Double Flash";
|
||||
DFlash.value = CASTOR3_STYLE_DFLASH;
|
||||
DFlash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
DFlash.speed_min = CASTOR3_SPEED_MIN;
|
||||
DFlash.speed_max = CASTOR3_SPEED_MAX;
|
||||
DFlash.speed = CASTOR3_SPEED_DEFAULT;
|
||||
DFlash.colors_min = 1;
|
||||
DFlash.colors_max = 1;
|
||||
DFlash.color_mode = MODE_COLORS_MODE_SPECIFIC;
|
||||
DFlash.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
DFlash.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
DFlash.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
DFlash.colors.resize(1);
|
||||
modes.push_back(DFlash);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Cycle — firmware colors, speed only |
|
||||
\*---------------------------------------------------------*/
|
||||
mode Cycle;
|
||||
Cycle.name = "Spectrum Cycle";
|
||||
Cycle.value = CASTOR3_STYLE_CYCLE;
|
||||
Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Cycle.speed_min = CASTOR3_SPEED_MIN;
|
||||
Cycle.speed_max = CASTOR3_SPEED_MAX;
|
||||
Cycle.speed = CASTOR3_SPEED_DEFAULT;
|
||||
Cycle.color_mode = MODE_COLORS_NONE;
|
||||
Cycle.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
Cycle.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
Cycle.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
modes.push_back(Cycle);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Gradient — single color, speed + brightness |
|
||||
| Note: b4 = brightness on wire (special case) |
|
||||
\*---------------------------------------------------------*/
|
||||
mode Gradient;
|
||||
Gradient.name = "Gradient";
|
||||
Gradient.value = CASTOR3_STYLE_GRADIENT;
|
||||
Gradient.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Gradient.speed_min = CASTOR3_SPEED_MIN;
|
||||
Gradient.speed_max = CASTOR3_SPEED_MAX;
|
||||
Gradient.speed = CASTOR3_SPEED_DEFAULT;
|
||||
Gradient.colors_min = 1;
|
||||
Gradient.colors_max = 1;
|
||||
Gradient.color_mode = MODE_COLORS_MODE_SPECIFIC;
|
||||
Gradient.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
Gradient.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
Gradient.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
Gradient.colors.resize(1);
|
||||
modes.push_back(Gradient);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Color Shift — 8-color palette, speed + brightness |
|
||||
\*---------------------------------------------------------*/
|
||||
mode ColorShift;
|
||||
ColorShift.name = "Color Shift";
|
||||
ColorShift.value = CASTOR3_STYLE_COLORSHIFT;
|
||||
ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
ColorShift.speed_min = CASTOR3_SPEED_MIN;
|
||||
ColorShift.speed_max = CASTOR3_SPEED_MAX;
|
||||
ColorShift.speed = CASTOR3_SPEED_DEFAULT;
|
||||
ColorShift.colors_min = 1;
|
||||
ColorShift.colors_max = 8;
|
||||
ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC;
|
||||
ColorShift.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
ColorShift.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
ColorShift.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
ColorShift.colors.resize(8);
|
||||
ColorShift.colors[0] = ToRGBColor(0xFF, 0x00, 0x00);
|
||||
ColorShift.colors[1] = ToRGBColor(0xFF, 0x72, 0x00);
|
||||
ColorShift.colors[2] = ToRGBColor(0xFF, 0xFF, 0x00);
|
||||
ColorShift.colors[3] = ToRGBColor(0x00, 0xFF, 0x00);
|
||||
ColorShift.colors[4] = ToRGBColor(0x00, 0xFF, 0xFF);
|
||||
ColorShift.colors[5] = ToRGBColor(0x00, 0x00, 0xFF);
|
||||
ColorShift.colors[6] = ToRGBColor(0xFF, 0x00, 0xFF);
|
||||
ColorShift.colors[7] = ToRGBColor(0xFF, 0x80, 0x80);
|
||||
modes.push_back(ColorShift);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Wave — firmware colors, speed only |
|
||||
\*---------------------------------------------------------*/
|
||||
mode Wave;
|
||||
Wave.name = "Wave";
|
||||
Wave.value = CASTOR3_STYLE_WAVE;
|
||||
Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Wave.speed_min = CASTOR3_SPEED_MIN;
|
||||
Wave.speed_max = CASTOR3_SPEED_MAX;
|
||||
Wave.speed = CASTOR3_SPEED_DEFAULT;
|
||||
Wave.color_mode = MODE_COLORS_NONE;
|
||||
Wave.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
Wave.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
Wave.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
modes.push_back(Wave);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Rainbow — firmware colors, speed only |
|
||||
\*---------------------------------------------------------*/
|
||||
mode Rainbow;
|
||||
Rainbow.name = "Rainbow";
|
||||
Rainbow.value = CASTOR3_STYLE_RAINBOW;
|
||||
Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Rainbow.speed_min = CASTOR3_SPEED_MIN;
|
||||
Rainbow.speed_max = CASTOR3_SPEED_MAX;
|
||||
Rainbow.speed = CASTOR3_SPEED_DEFAULT;
|
||||
Rainbow.color_mode = MODE_COLORS_NONE;
|
||||
Rainbow.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
Rainbow.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
Rainbow.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
modes.push_back(Rainbow);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Tri-Color — 3-color palette, speed + brightness |
|
||||
\*---------------------------------------------------------*/
|
||||
mode TriColor;
|
||||
TriColor.name = "Tri-Color";
|
||||
TriColor.value = CASTOR3_STYLE_TRICOLOR;
|
||||
TriColor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
TriColor.speed_min = CASTOR3_SPEED_MIN;
|
||||
TriColor.speed_max = CASTOR3_SPEED_MAX;
|
||||
TriColor.speed = CASTOR3_SPEED_DEFAULT;
|
||||
TriColor.colors_min = 1;
|
||||
TriColor.colors_max = 3;
|
||||
TriColor.color_mode = MODE_COLORS_MODE_SPECIFIC;
|
||||
TriColor.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
TriColor.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
TriColor.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
TriColor.colors.resize(3);
|
||||
TriColor.colors[0] = ToRGBColor(0x00, 0x00, 0xFF);
|
||||
TriColor.colors[1] = ToRGBColor(0x7D, 0x00, 0xFF);
|
||||
TriColor.colors[2] = ToRGBColor(0xFF, 0x00, 0xFF);
|
||||
modes.push_back(TriColor);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Spin — 3-color palette, speed + brightness |
|
||||
\*---------------------------------------------------------*/
|
||||
mode Spin;
|
||||
Spin.name = "Spin";
|
||||
Spin.value = CASTOR3_STYLE_SPIN;
|
||||
Spin.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Spin.speed_min = CASTOR3_SPEED_MIN;
|
||||
Spin.speed_max = CASTOR3_SPEED_MAX;
|
||||
Spin.speed = CASTOR3_SPEED_DEFAULT;
|
||||
Spin.colors_min = 1;
|
||||
Spin.colors_max = 3;
|
||||
Spin.color_mode = MODE_COLORS_MODE_SPECIFIC;
|
||||
Spin.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
Spin.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
Spin.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
Spin.colors.resize(3);
|
||||
Spin.colors[0] = ToRGBColor(0xFF, 0x00, 0xFE);
|
||||
Spin.colors[1] = ToRGBColor(0x00, 0xFF, 0xFB);
|
||||
Spin.colors[2] = ToRGBColor(0xFF, 0xFF, 0x00);
|
||||
modes.push_back(Spin);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Switch — 2-color palette, speed + brightness |
|
||||
\*---------------------------------------------------------*/
|
||||
mode Switch;
|
||||
Switch.name = "Switch";
|
||||
Switch.value = CASTOR3_STYLE_SWITCH;
|
||||
Switch.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Switch.speed_min = CASTOR3_SPEED_MIN;
|
||||
Switch.speed_max = CASTOR3_SPEED_MAX;
|
||||
Switch.speed = CASTOR3_SPEED_DEFAULT;
|
||||
Switch.colors_min = 1;
|
||||
Switch.colors_max = 2;
|
||||
Switch.color_mode = MODE_COLORS_MODE_SPECIFIC;
|
||||
Switch.brightness_min = CASTOR3_BRIGHTNESS_MIN;
|
||||
Switch.brightness_max = CASTOR3_BRIGHTNESS_MAX;
|
||||
Switch.brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
Switch.colors.resize(2);
|
||||
Switch.colors[0] = ToRGBColor(0xFF, 0x00, 0xFE);
|
||||
Switch.colors[1] = ToRGBColor(0x00, 0xFF, 0xFB);
|
||||
modes.push_back(Switch);
|
||||
|
||||
SetupZones();
|
||||
}
|
||||
|
||||
RGBController_GigabyteCastor3::~RGBController_GigabyteCastor3()
|
||||
{
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void RGBController_GigabyteCastor3::SetupZones()
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Single zone: LED ring + fans (not independently |
|
||||
| addressable — fans follow the pump ring effect) |
|
||||
| |
|
||||
| This is a SINGLE zone because the protocol does not |
|
||||
| expose per-LED addressing. All LEDs display the same |
|
||||
| hardware effect simultaneously. |
|
||||
\*---------------------------------------------------------*/
|
||||
zone led_ring;
|
||||
led_ring.name = "LED Ring";
|
||||
led_ring.type = ZONE_TYPE_SINGLE;
|
||||
led_ring.leds_min = 1;
|
||||
led_ring.leds_max = 1;
|
||||
led_ring.leds_count = 1;
|
||||
led_ring.matrix_map = NULL;
|
||||
zones.push_back(led_ring);
|
||||
|
||||
led new_led;
|
||||
new_led.name = "LED Ring";
|
||||
leds.push_back(new_led);
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
void RGBController_GigabyteCastor3::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Fixed zone size — not resizable |
|
||||
\*---------------------------------------------------------*/
|
||||
}
|
||||
|
||||
void RGBController_GigabyteCastor3::DeviceUpdateLEDs()
|
||||
{
|
||||
DeviceUpdateMode();
|
||||
}
|
||||
|
||||
void RGBController_GigabyteCastor3::UpdateZoneLEDs(int /*zone*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_GigabyteCastor3::UpdateSingleLED(int /*led*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_GigabyteCastor3::DeviceUpdateMode()
|
||||
{
|
||||
unsigned char style = (unsigned char)modes[active_mode].value;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Handle Off mode (sentinel value 0xFF) |
|
||||
\*---------------------------------------------------------*/
|
||||
if(style == 0xFF)
|
||||
{
|
||||
controller->SetOff();
|
||||
return;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Determine speed, brightness, b4, b5, and color type |
|
||||
| from the effect lookup table matching castor3.py |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned char speed = CASTOR3_SPEED_DEFAULT;
|
||||
unsigned char brightness = CASTOR3_BRIGHTNESS_DEFAULT;
|
||||
|
||||
if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED)
|
||||
{
|
||||
speed = (unsigned char)modes[active_mode].speed;
|
||||
}
|
||||
|
||||
if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS)
|
||||
{
|
||||
brightness = (unsigned char)modes[active_mode].brightness;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Look up b4, b5, and color_type per effect |
|
||||
| These match the LED_EFFECTS dict in castor3.py |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned char b4 = 0x02;
|
||||
unsigned char b5 = 0x01;
|
||||
castor3_color_type color_type = CASTOR3_COLORS_NONE;
|
||||
|
||||
switch(style)
|
||||
{
|
||||
case CASTOR3_STYLE_STATIC:
|
||||
case CASTOR3_STYLE_PULSE:
|
||||
case CASTOR3_STYLE_FLASH:
|
||||
case CASTOR3_STYLE_DFLASH:
|
||||
b4 = 0x02;
|
||||
b5 = 0x01;
|
||||
color_type = CASTOR3_COLORS_SINGLE;
|
||||
break;
|
||||
|
||||
case CASTOR3_STYLE_GRADIENT:
|
||||
/*-------------------------------------------------*\
|
||||
| Gradient: b4 = brightness on wire (special case) |
|
||||
\*-------------------------------------------------*/
|
||||
b4 = brightness;
|
||||
b5 = 0x01;
|
||||
color_type = CASTOR3_COLORS_SINGLE;
|
||||
break;
|
||||
|
||||
case CASTOR3_STYLE_CYCLE:
|
||||
case CASTOR3_STYLE_WAVE:
|
||||
case CASTOR3_STYLE_RAINBOW:
|
||||
b4 = 0x02;
|
||||
b5 = 0x01;
|
||||
color_type = CASTOR3_COLORS_NONE;
|
||||
break;
|
||||
|
||||
case CASTOR3_STYLE_COLORSHIFT:
|
||||
b4 = 0x08; /* ClrCount=8 */
|
||||
b5 = 0x02; /* CmbIndex+1=2 */
|
||||
color_type = CASTOR3_COLORS_PALETTE;
|
||||
break;
|
||||
|
||||
case CASTOR3_STYLE_TRICOLOR:
|
||||
case CASTOR3_STYLE_SPIN:
|
||||
b4 = 0x02;
|
||||
b5 = 0x01;
|
||||
color_type = CASTOR3_COLORS_PALETTE;
|
||||
break;
|
||||
|
||||
case CASTOR3_STYLE_SWITCH:
|
||||
b4 = 0x02;
|
||||
b5 = 0x01;
|
||||
color_type = CASTOR3_COLORS_PALETTE;
|
||||
break;
|
||||
}
|
||||
|
||||
controller->SetEffect(style, speed, brightness, b4, b5,
|
||||
color_type, modes[active_mode].colors);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_GigabyteCastor3.h |
|
||||
| |
|
||||
| RGBController for Gigabyte Aorus Waterforce X II 360 |
|
||||
| AIO Cooler (Castor3 controller) |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RGBController.h"
|
||||
#include "GigabyteCastor3Controller.h"
|
||||
|
||||
class RGBController_GigabyteCastor3 : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_GigabyteCastor3(GigabyteCastor3Controller* controller_ptr);
|
||||
~RGBController_GigabyteCastor3();
|
||||
|
||||
void SetupZones();
|
||||
void ResizeZone(int zone, int new_size);
|
||||
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
|
||||
void DeviceUpdateMode();
|
||||
|
||||
private:
|
||||
GigabyteCastor3Controller* controller;
|
||||
};
|
||||
+17
-2
@@ -68,9 +68,14 @@ void RGBFusion2BlackwellGPUController::SetMode(uint8_t type, uint8_t zone, uint8
|
||||
if(zone_config.numberOfColors > 0)
|
||||
{
|
||||
int currentPos = 12;
|
||||
if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT)
|
||||
switch(gpu_layout)
|
||||
{
|
||||
currentPos = 11;
|
||||
case RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT:
|
||||
case RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT:
|
||||
currentPos = 11;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < zone_config.numberOfColors; i++)
|
||||
@@ -90,6 +95,16 @@ void RGBFusion2BlackwellGPUController::SetZone(uint8_t zone, uint8_t mode, fusio
|
||||
if(mode == RGB_FUSION2_BLACKWELL_GPU_MODE_BREATHING)
|
||||
zone_config.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX;
|
||||
|
||||
switch(gpu_layout)
|
||||
{
|
||||
case RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT:
|
||||
if(mode == RGB_FUSION2_BLACKWELL_GPU_MODE_DIRECT)
|
||||
mode = RGB_FUSION2_BLACKWELL_GPU_MODE_STATIC;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t type = RGB_FUSION2_BLACKWELL_GPU_REG_COLOR;
|
||||
if(mode != RGB_FUSION2_BLACKWELL_GPU_MODE_DIRECT)
|
||||
type = RGB_FUSION2_BLACKWELL_GPU_REG_MODE;
|
||||
|
||||
+6
-5
@@ -62,11 +62,12 @@ enum
|
||||
|
||||
enum
|
||||
{
|
||||
RGB_FUSION2_BLACKWELL_GPU_SINGLE_ZONE = 0,
|
||||
RGB_FUSION2_BLACKWELL_GPU_GAMING_LAYOUT = 1,
|
||||
RGB_FUSION2_BLACKWELL_GPU_WATERFORCE_LAYOUT = 2,
|
||||
RGB_FUSION2_BLACKWELL_GPU_AORUS_WATERFORCE_LAYOUT = 3,
|
||||
RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT = 4,
|
||||
RGB_FUSION2_BLACKWELL_GPU_SINGLE_ZONE = 0,
|
||||
RGB_FUSION2_BLACKWELL_GPU_GAMING_LAYOUT = 1,
|
||||
RGB_FUSION2_BLACKWELL_GPU_WATERFORCE_LAYOUT = 2,
|
||||
RGB_FUSION2_BLACKWELL_GPU_AORUS_WATERFORCE_LAYOUT = 3,
|
||||
RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT = 4,
|
||||
RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT = 5,
|
||||
};
|
||||
|
||||
class RGBFusion2BlackwellGPUController
|
||||
|
||||
+38
-19
@@ -1,4 +1,4 @@
|
||||
/*---------------------------------------------------------*\
|
||||
/*---------------------------------------------------------*\
|
||||
| GigabyteRGBFusion2BlackwellGPUControllerDetect.cpp |
|
||||
| |
|
||||
| Detector for Gigabyte RGB Fusion 2 Blackwell GPU |
|
||||
@@ -181,29 +181,48 @@ void DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers(i2c_sm
|
||||
DetectGigabyteRGBFusion2BlackwellGPUControllers(bus, i2c_addr, name, RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT);
|
||||
} /* DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers() */
|
||||
|
||||
/*******************************************************************************************\
|
||||
* *
|
||||
* DetectGigabyteRGBFusion2BlackwellAorusMaster5090DV2IceLayoutGPUControllers *
|
||||
* *
|
||||
* Detect GigabyteRGB Fusion2 controllers with AORUS master 5090 D V2 ICE layout on *
|
||||
* enumerated I2C busses. *
|
||||
* *
|
||||
* bus - pointer to i2c_smbus_interface where RGB Fusion2 device is connected *
|
||||
* dev - I2C address of RGB Fusion2 device *
|
||||
* *
|
||||
\*******************************************************************************************/
|
||||
|
||||
void DetectGigabyteRGBFusion2BlackwellAorusMaster5090DV2IceLayoutGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name)
|
||||
{
|
||||
DetectGigabyteRGBFusion2BlackwellGPUControllers(bus, i2c_addr, name, RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT);
|
||||
} /* DetectGigabyteRGBFusion2BlackwellAorusMaster5090DV2IceLayoutGPUControllers() */
|
||||
|
||||
/*-----------------------------------------*\
|
||||
| Nvidia GPUs |
|
||||
\*-----------------------------------------*/
|
||||
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5060 Ti Gaming OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5060TI_GAMING_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Aero OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_AERO_OC_12G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Eagle OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_EAGLE_OC_12G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_GAMING_OC_12G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Eagle OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_EAGLE_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Eagle OC ICE", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_EAGLE_OC_ICE_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Aero OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_AERO_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_GAMING_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 Aero OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_AERO_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_GAMING_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 MASTER", DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_MASTER_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellAorusWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_GAMING_OC_32G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_XTREME_WATERFORCE_32G_SUB_DEV1, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_XTREME_WATERFORCE_32G_SUB_DEV2, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 MASTER", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090_MASTER_32G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 MASTER ICE", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090_MASTER_ICE_32G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5060 Ti Gaming OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5060TI_GAMING_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Aero OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_AERO_OC_12G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Eagle OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_EAGLE_OC_12G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Eagle OC ICE", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_EAGLE_OC_ICE_12G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_GAMING_OC_12G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Eagle OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_EAGLE_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Eagle OC ICE", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_EAGLE_OC_ICE_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Aero OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_AERO_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_GAMING_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 Aero OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_AERO_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_GAMING_OC_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 MASTER", DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_MASTER_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellAorusWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 MASTER ICE", DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_MASTER_ICE_16G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_GAMING_OC_32G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_XTREME_WATERFORCE_32G_SUB_DEV1, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_XTREME_WATERFORCE_32G_SUB_DEV2, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 MASTER", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090_MASTER_32G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 MASTER ICE", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090_MASTER_ICE_32G_SUB_DEV, 0x75);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 D V2 MASTER ICE", DetectGigabyteRGBFusion2BlackwellAorusMaster5090DV2IceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090D_V2_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090D_V2_MASTER_ICE_24G_SUB_DEV, 0x75);
|
||||
|
||||
/*-----------------------------------------*\
|
||||
| AMD GPUs |
|
||||
|
||||
+52
-1
@@ -400,6 +400,52 @@ void RGBController_RGBFusion2BlackwellGPU::SetupZones()
|
||||
leds.push_back(top_led);
|
||||
}
|
||||
|
||||
else if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT)
|
||||
{
|
||||
const char * fan_names[] = { "Right Fan", "Left Fan", "Middle Fan" };
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
zone fan_zone;
|
||||
fan_zone.name = fan_names[i];
|
||||
fan_zone.type = ZONE_TYPE_SINGLE;
|
||||
fan_zone.leds_min = 1;
|
||||
fan_zone.leds_max = 1;
|
||||
fan_zone.leds_count = 1;
|
||||
fan_zone.matrix_map = NULL;
|
||||
zones.push_back(fan_zone);
|
||||
|
||||
led new_led;
|
||||
new_led.name = fan_names[i];
|
||||
leds.push_back(new_led);
|
||||
}
|
||||
|
||||
zone backplate;
|
||||
backplate.name = "Backplate";
|
||||
backplate.type = ZONE_TYPE_SINGLE;
|
||||
backplate.leds_min = 1;
|
||||
backplate.leds_max = 1;
|
||||
backplate.leds_count = 1;
|
||||
backplate.matrix_map = NULL;
|
||||
zones.push_back(backplate);
|
||||
|
||||
led bp_led;
|
||||
bp_led.name = "Backplate";
|
||||
leds.push_back(bp_led);
|
||||
|
||||
zone side_logo;
|
||||
side_logo.name = "Side Logo";
|
||||
side_logo.type = ZONE_TYPE_SINGLE;
|
||||
side_logo.leds_min = 1;
|
||||
side_logo.leds_max = 1;
|
||||
side_logo.leds_count = 1;
|
||||
side_logo.matrix_map = NULL;
|
||||
zones.push_back(side_logo);
|
||||
|
||||
led sl_led;
|
||||
sl_led.name = "Side Logo";
|
||||
leds.push_back(sl_led);
|
||||
}
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
@@ -446,6 +492,10 @@ void RGBController_RGBFusion2BlackwellGPU::DeviceUpdateLEDs()
|
||||
gpu_zones = 6; // Zones 4 and 5 refer to ui zone 4
|
||||
break;
|
||||
|
||||
case RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT:
|
||||
gpu_zones = 6;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_TRACE("[%s] Invalid GPU layout (%d) when updating LEDs.", name.c_str(), gpu_layout);
|
||||
return; // should not happen
|
||||
@@ -472,7 +522,8 @@ void RGBController_RGBFusion2BlackwellGPU::DeviceUpdateLEDs()
|
||||
}
|
||||
ui_zone_idx = zone_idx - 1; // Map: HW zone 1->UI zone 0, HW zone 2->UI zone 1, HW zone 3->UI zone 2, HW zone 4->UI zone 3
|
||||
}
|
||||
else if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT)
|
||||
else if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT ||
|
||||
gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT)
|
||||
{
|
||||
if(zone_idx == 5)
|
||||
{
|
||||
|
||||
@@ -152,6 +152,7 @@ REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3090 XTREME WATERFORCE",
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3090 XTREME WATERFORCE WB", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3090_XTREME_WATERFORCE_WB_SUB_DEV, 0x64);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070_GAMING_OC_12G, 0x71);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070_GAMING_OC_12G_V2, 0x71);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_AD103_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070_GAMING_OC_12G_V2, 0x71);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Geforce RTX 4070 Aero OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070_AERO_OC_12G_SUB_DEV, 0x71);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 SUPER GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070S_GAMING_OC_12G, 0x71);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 SUPER Aero OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070S_AERO_OC_12G, 0x71);
|
||||
@@ -188,12 +189,14 @@ REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7600 GAMING OC 8G",
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7600 GAMING OC 8G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI33_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7600_GAMING_OC_8G_SUB_DEV2, 0x55);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7600 XT GAMING OC 16G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI33_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7600XT_GAMING_OC_16G_SUB_DEV, 0x55);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 6700 XT GAMING OC 12G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX6700XT_GAMING_OC_12G_SUB_DEV, 0x62);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 6800 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, GIGABYTE_SUB_VEN, GIGABYTE_RX6800XT_GAMING_OC_SUB_DEV, 0x62);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 6900 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, GIGABYTE_SUB_VEN, GIGABYTE_RX6900XT_GAMING_OC_SUB_DEV, 0x62);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7700 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7700XT_GAMING_OC_SUB_DEV, 0x62);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7800 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7800XT_GAMING_OC_16G_SUB_DEV, 0x62);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 GRE GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900GRE_GAMING_OC_16G_SUB_DEV, 0x62);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900XT_GAMING_OC_20G_SUB_DEV, 0x62);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 XTX GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900XTX_GAMING_OC_24G_SUB_DEV, 0x62);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS Radeon RX 7900 XTX ELITE 24G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX7900XTX_ELITE_24G_SUB_DEV, 0x71);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS RX 6750 XT ELITE 12G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX_6750_XT_ELITE_12G_SUB_DEV, 0x70);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS RX 6900 XT EXTREME WATERFORCE WB", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV2, GIGABYTE_SUB_VEN, GIGABYTE_RX6900XT_XTREME_WATERFORCE_WB_SUB_DEV, 0x70);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS Radeon RX 9070 XT Elite", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX9070XT_ELITE_16G_SUB_DEV, 0x73);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@
|
||||
#include <map>
|
||||
#include "RGBController.h"
|
||||
|
||||
#define GB_FUSION2_ZONES_MAX 8
|
||||
#define GB_FUSION2_ZONES_MAX 12
|
||||
|
||||
/*--------------------------------------------------------*\
|
||||
| Base LED mappings found on all controllers. |
|
||||
@@ -89,6 +89,23 @@ const FwdLedHeaders LedLookup =
|
||||
\*-------------------------------------------------*/
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------*\
|
||||
| The layout_id masks for supported effects. |
|
||||
\*--------------------------------------------------------*/
|
||||
enum GB_LID_EFFECTS_MASKS : uint32_t
|
||||
{
|
||||
GB_EFF_BREATH = 0x001,
|
||||
GB_EFF_BEAT = 0x002,
|
||||
GB_EFF_CYCLE = 0x004,
|
||||
GB_EFF_FLASH = 0x008,
|
||||
GB_EFF_RANDOM = 0x010,
|
||||
GB_EFF_WAVE = 0x020,
|
||||
GB_EFF_DFLASH = 0x040,
|
||||
GB_EFF_WAVE1 = 0x080,
|
||||
GB_EFF_WAVE2 = 0x100,
|
||||
GB_EFF_CORE_MASK = 0x1FF
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GB_FUSION2_LED_IDX idx;
|
||||
|
||||
@@ -95,10 +95,9 @@ bool RGBFusion2USBController::RefreshHardwareInfo()
|
||||
return false;
|
||||
}
|
||||
|
||||
IT8297Report report;
|
||||
std::memcpy(&report, buffer, sizeof(IT8297Report));
|
||||
report_loaded = true;
|
||||
|
||||
device_num = report.device_num;
|
||||
description = std::string(report.str_product, 28);
|
||||
if(std::string::iterator nul = std::find(description.begin(), description.end(), '\0');
|
||||
nul != description.end())
|
||||
@@ -318,7 +317,7 @@ bool RGBFusion2USBController::SetCalibration(const EncodedCalibration& cal, bool
|
||||
cal_data.mainboard = desired.c.rgb_cali;
|
||||
cal_data.spare[0] = desired.c.c_spare0;
|
||||
cal_data.spare[1] = desired.c.c_spare1;
|
||||
|
||||
|
||||
if(product_id == 0x5711)
|
||||
{
|
||||
cal_data.dled[2] = desired.c.d_strip_c2;
|
||||
@@ -362,7 +361,6 @@ void RGBFusion2USBController::SetLedCount(unsigned int c0, unsigned int c1, unsi
|
||||
\*---------------------------------------------------------*/
|
||||
bool RGBFusion2USBController::SetStripBuiltinEffectState(int hdr, bool enable)
|
||||
{
|
||||
static bool first_call = true;
|
||||
int bitmask = 0;
|
||||
|
||||
if(hdr == -1)
|
||||
@@ -392,16 +390,16 @@ bool RGBFusion2USBController::SetStripBuiltinEffectState(int hdr, bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
int new_effect_disabled = enable
|
||||
? (effect_disabled & ~bitmask)
|
||||
: (effect_disabled | bitmask);
|
||||
int base_mask = (effect_disabled < 0) ? 0 : effect_disabled;
|
||||
int new_effect_disabled = enable
|
||||
? (base_mask & ~bitmask)
|
||||
: (base_mask | bitmask);
|
||||
|
||||
if(!first_call && new_effect_disabled == effect_disabled)
|
||||
// Skip redundant writes only after we have synchronized at least once
|
||||
if(effect_disabled >= 0 && new_effect_disabled == effect_disabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
first_call = false;
|
||||
effect_disabled = new_effect_disabled;
|
||||
int res = SendCCReport(0x32, effect_disabled);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
@@ -439,7 +437,7 @@ bool RGBFusion2USBController::EnableLampArray(bool enable)
|
||||
{
|
||||
return SendCCReport(0x48, enable ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
std::string RGBFusion2USBController::GetDeviceName()
|
||||
{
|
||||
return(name);
|
||||
@@ -703,3 +701,115 @@ void RGBFusion2USBController::ResetController()
|
||||
}
|
||||
ApplyEffect(true);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Check controller for gen2 ARGB support |
|
||||
| Checks for supported device number |
|
||||
| Then checks for supported controllers |
|
||||
| Then checks for supported feature bit (SaveLEDState) |
|
||||
| Finally checks for strip detection value. |
|
||||
\*---------------------------------------------------------*/
|
||||
bool RGBFusion2USBController::SupportsGen2() const
|
||||
{
|
||||
bool supports_gen2 = false;
|
||||
|
||||
supports_gen2 = (device_num == 0x00)
|
||||
&& (product_id == 0x5702 || product_id==0x5711 || product_id==0x8950)
|
||||
&& (report.support_cmd_flag & 0x01)
|
||||
&& (report.strip_detect == 0x01);
|
||||
|
||||
return supports_gen2;
|
||||
}
|
||||
|
||||
std::vector<Gen2StripInfo> RGBFusion2USBController::ExportGen2Strips() const
|
||||
{
|
||||
size_t count = (product_id == 0x5711) ? 4u : 2u;
|
||||
std::vector<Gen2StripInfo> out;
|
||||
out.reserve(count);
|
||||
for(size_t i = 0; i < count; ++i)
|
||||
{
|
||||
out.push_back(g2_strip_info[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Scan Headers for Gen2 Devices |
|
||||
\*---------------------------------------------------------*/
|
||||
bool RGBFusion2USBController::ScanGen2Strips()
|
||||
{
|
||||
for(unsigned i = 0; i < 4; ++i)
|
||||
{
|
||||
if(g2_strip_info[i].LedsOfStrip.capacity() < 15)
|
||||
{
|
||||
g2_strip_info[i].LedsOfStrip.reserve(15);
|
||||
}
|
||||
g2_strip_info[i].numStrip = 0;
|
||||
g2_strip_info[i].totalLeds = 0;
|
||||
g2_strip_info[i].LedsOfStrip.resize(0);
|
||||
}
|
||||
|
||||
const unsigned int hdr_lim = (product_id == 0x5711) ? 4u : 2u;
|
||||
|
||||
for(unsigned int slot = 0; slot < hdr_lim; ++slot)
|
||||
{
|
||||
static constexpr uint8_t delta[4] = {4, 5, 0, 1};
|
||||
uint8_t scan_cmd = static_cast<uint8_t>(GEN2_LED_BASE_SCAN + delta[slot]);
|
||||
uint8_t info_cmd = static_cast<uint8_t>(scan_cmd + 2);
|
||||
|
||||
if(!SendCCReport(scan_cmd, 0x00, 0x00))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(700));
|
||||
|
||||
if(!SendCCReport(info_cmd, 0x00, 0x00))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char feature_buf[64] = {0};
|
||||
feature_buf[0] = report_id;
|
||||
int recv_len = hid_get_feature_report(dev, feature_buf, sizeof(feature_buf));
|
||||
if(recv_len < 64)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int seg_count = static_cast<int>(feature_buf[1]);
|
||||
if(seg_count < 0)
|
||||
{
|
||||
seg_count = 0;
|
||||
}
|
||||
if(seg_count > 15)
|
||||
{
|
||||
seg_count = 15;
|
||||
}
|
||||
Gen2StripInfo& dst = g2_strip_info[slot];
|
||||
dst.numStrip = static_cast<uint8_t>(seg_count);
|
||||
dst.LedsOfStrip.resize(static_cast<size_t>(seg_count));
|
||||
|
||||
uint32_t total_leds = 0;
|
||||
const int counts_base = 2;
|
||||
|
||||
for(int k = 0; k < seg_count; ++k)
|
||||
{
|
||||
const int off = counts_base + (k * 2);
|
||||
uint16_t lo = static_cast<uint16_t>(feature_buf[off + 0]);
|
||||
uint16_t hi = static_cast<uint16_t>(feature_buf[off + 1]);
|
||||
uint16_t cnt = static_cast<uint16_t>(lo | (hi << 8));
|
||||
|
||||
dst.LedsOfStrip[static_cast<size_t>(k)] = cnt;
|
||||
total_leds += cnt;
|
||||
}
|
||||
|
||||
dst.totalLeds = total_leds;
|
||||
|
||||
SetLedCount(0, 0, 0, 0);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
SaveLEDState(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
|
||||
#define FUSION2_USB_BUFFER_SIZE 64
|
||||
|
||||
/*--------------------------------------------------------*\
|
||||
| Gen2 scan/info opcode base |
|
||||
\*--------------------------------------------------------*/
|
||||
#define GEN2_LED_BASE_SCAN 0x38
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Effects mode list |
|
||||
\*---------------------------------------------------------*/
|
||||
@@ -83,6 +88,16 @@ struct EncodedCalibration
|
||||
std::string mainboard;
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| High level struct to contain Gen2 Header Data |
|
||||
\*---------------------------------------------------------*/
|
||||
struct Gen2StripInfo
|
||||
{
|
||||
uint8_t numStrip = 0;
|
||||
std::vector<uint16_t> LedsOfStrip;
|
||||
uint32_t totalLeds = 0;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
@@ -321,6 +336,8 @@ public:
|
||||
void SetLEDEffect(int led, int mode, unsigned int speed, unsigned char brightness, bool random, uint32_t* color);
|
||||
bool SetStripBuiltinEffectState(int hdr, bool enable);
|
||||
void SetStripColors(unsigned int hdr, RGBColor * colors, unsigned int num_colors, int single_led = -1);
|
||||
bool SupportsGen2() const;
|
||||
bool ScanGen2Strips();
|
||||
|
||||
EncodedCalibration GetCalibration(bool refresh_from_hw = false);
|
||||
std::string GetDeviceName();
|
||||
@@ -330,6 +347,8 @@ public:
|
||||
std::string GetFWVersion();
|
||||
uint16_t GetProductID();
|
||||
std::string GetSerial();
|
||||
std::vector<Gen2StripInfo> ExportGen2Strips() const;
|
||||
Gen2StripInfo g2_strip_info[4];
|
||||
|
||||
private:
|
||||
std::string DecodeCalibrationBuffer(uint32_t value) const;
|
||||
@@ -345,7 +364,7 @@ private:
|
||||
int SendPacket(unsigned char* packet);
|
||||
|
||||
hid_device* dev;
|
||||
int device_num;
|
||||
uint8_t device_num;
|
||||
uint16_t product_id;
|
||||
uint32_t effect_zone_mask = 0;
|
||||
int mode;
|
||||
@@ -356,7 +375,7 @@ private:
|
||||
std::string location;
|
||||
std::string version;
|
||||
std::string chip_id;
|
||||
int effect_disabled = 0;
|
||||
int effect_disabled = -1;
|
||||
int report_id = 0xCC;
|
||||
bool report_loaded = false;
|
||||
bool cali_loaded = false;
|
||||
|
||||
+197
-59
@@ -25,7 +25,7 @@
|
||||
@effects :white_check_mark:
|
||||
@detectors DetectGigabyteRGBFusion2USBControllers
|
||||
@comment The Fusion 2 USB controller applies to most AMD and
|
||||
Intel mainboards from the x570 and z390 chipsets onwards.
|
||||
Intel mainboards from the X570 and z390 chipsets onwards.
|
||||
\*-------------------------------------------------------------------*/
|
||||
|
||||
RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController* controller_ptr, std::string detector)
|
||||
@@ -39,8 +39,10 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
|
||||
version = controller->GetFWVersion();
|
||||
location = controller->GetDeviceLocation();
|
||||
serial = controller->GetSerial();
|
||||
product_id = controller->GetProductID();
|
||||
device_num = controller->GetDeviceNum();
|
||||
|
||||
|
||||
mode Direct;
|
||||
Direct.name = "Direct";
|
||||
Direct.value = 0xFFFF;
|
||||
@@ -206,6 +208,13 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
|
||||
|
||||
RGBController_RGBFusion2USB::~RGBController_RGBFusion2USB()
|
||||
{
|
||||
// Free any zones we allocated for the per-instance layout
|
||||
for(gb_fusion2_zone* z : allocated_zones)
|
||||
{
|
||||
delete z;
|
||||
}
|
||||
allocated_zones.clear();
|
||||
|
||||
delete controller;
|
||||
}
|
||||
|
||||
@@ -214,94 +223,125 @@ RGBController_RGBFusion2USB::~RGBController_RGBFusion2USB()
|
||||
\*---------------------------------------------------------*/
|
||||
void RGBController_RGBFusion2USB::Init_Controller()
|
||||
{
|
||||
const std::string SectionCustom = "CustomLayout";
|
||||
|
||||
const gb_fusion2_device* src_layout = gb_fusion2_device_list[device_index];
|
||||
const std::string SectionGen2 = "Gigabyte-Gen2-ARGB";
|
||||
const std::string SectionCustomBase = "CustomLayout";
|
||||
const std::string SectionCustom = SectionCustomBase + std::to_string(device_num);
|
||||
const std::string SectionCalibration = "Calibration";
|
||||
RvrseLedHeaders ReverseLedLookup = reverse_map(LedLookup);
|
||||
SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager();
|
||||
nlohmann::json device_settings = settings_manager->GetSettings(detector_name);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Create the custom layout from the generic_device |
|
||||
| Checks for Gen2 support and adds flag to json. |
|
||||
\*---------------------------------------------------------*/
|
||||
gb_fusion2_device* layout = const_cast<gb_fusion2_device*>(gb_fusion2_device_list[device_index]);
|
||||
if(controller->SupportsGen2())
|
||||
{
|
||||
if(!device_settings.contains(SectionGen2))
|
||||
{
|
||||
device_settings[SectionGen2]["Enabled"] = false;
|
||||
settings_manager->SetSettings(detector_name, device_settings);
|
||||
settings_manager->SaveSettings();
|
||||
}
|
||||
|
||||
supports_gen2 = device_settings[SectionGen2]["Enabled"];
|
||||
|
||||
if(supports_gen2)
|
||||
{
|
||||
controller->ScanGen2Strips();
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Create the custom layout from the generic layout |
|
||||
\*---------------------------------------------------------*/
|
||||
switch(product_id)
|
||||
{
|
||||
case 0x8950:
|
||||
src_layout = gb_fusion2_device_list[device_index + 1];
|
||||
break;
|
||||
case 0x5711:
|
||||
src_layout = gb_fusion2_device_list[device_index + 2];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!device_settings.contains(SectionCustom))
|
||||
{
|
||||
device_settings[SectionCustom]["Enabled"] = false;
|
||||
device_settings[SectionCustom]["Data"] = BuildCustomLayoutJson(layout, ReverseLedLookup);
|
||||
device_settings[SectionCustom]["Data"] = BuildCustomLayoutJson(src_layout, ReverseLedLookup);
|
||||
settings_manager->SetSettings(detector_name, device_settings);
|
||||
settings_manager->SaveSettings();
|
||||
}
|
||||
|
||||
bool custom_layout = device_settings[SectionCustom]["Enabled"];
|
||||
|
||||
if(custom_layout)
|
||||
{
|
||||
LoadCustomLayoutFromJson(device_settings[SectionCustom]["Data"], LedLookup, layout);
|
||||
}
|
||||
|
||||
EncodedCalibration hw_cal = controller->GetCalibration(false);
|
||||
|
||||
if(!device_settings.contains(SectionCalibration))
|
||||
if(device_num == 0)
|
||||
{
|
||||
device_settings[SectionCalibration]["Enabled"] = false;
|
||||
device_settings[SectionCalibration]["Data"] = WriteCalJsonFrom(hw_cal);
|
||||
settings_manager->SetSettings(detector_name, device_settings);
|
||||
settings_manager->SaveSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
nlohmann::json& cal_sec = device_settings[SectionCalibration];
|
||||
bool cal_enable = cal_sec.value("Enabled", false);
|
||||
|
||||
if(!cal_sec.contains("Data") || !cal_sec["Data"].is_object())
|
||||
if(!device_settings.contains(SectionCalibration))
|
||||
{
|
||||
cal_sec["Data"] = WriteCalJsonFrom(hw_cal);
|
||||
device_settings[SectionCalibration]["Enabled"] = false;
|
||||
device_settings[SectionCalibration]["Data"] = WriteCalJsonFrom(hw_cal);
|
||||
settings_manager->SetSettings(detector_name, device_settings);
|
||||
settings_manager->SaveSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
nlohmann::json& cdata = cal_sec["Data"];
|
||||
FillMissingWith(cdata, hw_cal);
|
||||
nlohmann::json& cal_sec = device_settings[SectionCalibration];
|
||||
bool cal_enable = cal_sec.value("Enabled", false);
|
||||
|
||||
if(!cal_enable)
|
||||
if(!cal_sec.contains("Data") || !cal_sec["Data"].is_object())
|
||||
{
|
||||
cal_sec["Data"] = WriteCalJsonFrom(hw_cal);
|
||||
settings_manager->SetSettings(detector_name, device_settings);
|
||||
settings_manager->SaveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
if(cal_enable)
|
||||
{
|
||||
const nlohmann::json& cdata = cal_sec["Data"];
|
||||
|
||||
EncodedCalibration desired;
|
||||
desired.dled[0] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED1");
|
||||
desired.dled[1] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED2");
|
||||
desired.mainboard = GET_JSON_VAL_ELSE_OFF(cdata, "Mainboard");
|
||||
desired.spare[0] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare0");
|
||||
desired.spare[1] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare1");
|
||||
|
||||
if(controller->GetProductID() == 0x5711)
|
||||
{
|
||||
desired.dled[2] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED3");
|
||||
desired.dled[3] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED4");
|
||||
desired.spare[2] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare2");
|
||||
desired.spare[3] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare3");
|
||||
}
|
||||
else
|
||||
{
|
||||
desired.dled[2] = "OFF";
|
||||
desired.dled[3] = "OFF";
|
||||
desired.spare[2] = "OFF";
|
||||
desired.spare[3] = "OFF";
|
||||
nlohmann::json& cdata = cal_sec["Data"];
|
||||
FillMissingWith(cdata, hw_cal);
|
||||
|
||||
if(!cal_enable)
|
||||
{
|
||||
cal_sec["Data"] = WriteCalJsonFrom(hw_cal);
|
||||
settings_manager->SetSettings(detector_name, device_settings);
|
||||
settings_manager->SaveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
if(cal_enable)
|
||||
{
|
||||
const nlohmann::json& cdata = cal_sec["Data"];
|
||||
|
||||
EncodedCalibration desired;
|
||||
desired.dled[0] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED1");
|
||||
desired.dled[1] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED2");
|
||||
desired.mainboard = GET_JSON_VAL_ELSE_OFF(cdata, "Mainboard");
|
||||
desired.spare[0] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare0");
|
||||
desired.spare[1] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare1");
|
||||
|
||||
if(controller->GetProductID() == 0x5711)
|
||||
{
|
||||
desired.dled[2] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED3");
|
||||
desired.dled[3] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED4");
|
||||
desired.spare[2] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare2");
|
||||
desired.spare[3] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare3");
|
||||
}
|
||||
else
|
||||
{
|
||||
desired.dled[2] = "OFF";
|
||||
desired.dled[3] = "OFF";
|
||||
desired.spare[2] = "OFF";
|
||||
desired.spare[3] = "OFF";
|
||||
}
|
||||
controller->SetCalibration(desired, false);
|
||||
}
|
||||
controller->SetCalibration(desired, false);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*\
|
||||
| When no match found the first entry (generic_device) will be used |
|
||||
| otherwise look up channel map based on device name |
|
||||
@@ -315,35 +355,81 @@ void RGBController_RGBFusion2USB::Init_Controller()
|
||||
\*-----------------------------------------------------------------*/
|
||||
for(unsigned int i = 0; i < GB_FUSION2_DEVICE_COUNT; i++)
|
||||
{
|
||||
if(gb_fusion2_device_list[i]->name == name)
|
||||
if(gb_fusion2_device_list[i]->name == name &&
|
||||
gb_fusion2_device_list[i]->device_num == device_num)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Set device ID |
|
||||
\*---------------------------------------------------------*/
|
||||
device_index = i;
|
||||
layout = const_cast<gb_fusion2_device*>(gb_fusion2_device_list[i]);
|
||||
src_layout = gb_fusion2_device_list[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------*\
|
||||
| Creates per instance copy of layouts. |
|
||||
\*---------------------------------------------------------------------*/
|
||||
instance_layout.zones = &instance_zones;
|
||||
instance_layout.layout_id = src_layout->layout_id;
|
||||
instance_layout.device_num = src_layout->device_num;
|
||||
instance_layout.name = src_layout->name;
|
||||
|
||||
for(uint8_t zi = 0; zi < GB_FUSION2_ZONES_MAX; ++zi)
|
||||
{
|
||||
(*instance_layout.zones)[zi] = (*src_layout->zones)[zi];
|
||||
}
|
||||
|
||||
if(custom_layout)
|
||||
{
|
||||
LoadCustomLayoutFromJson(device_settings[SectionCustom]["Data"], LedLookup, &instance_layout);
|
||||
}
|
||||
/*---------------------------------------------------------------------*\
|
||||
| Culls the mode support based on layout_id. |
|
||||
\*---------------------------------------------------------------------*/
|
||||
const uint32_t effect_mask = instance_layout.layout_id & GB_EFF_CORE_MASK;
|
||||
modes.erase(std::remove_if(modes.begin(), modes.end(),
|
||||
[effect_mask](const mode& m)
|
||||
{
|
||||
if(m.value == 0xFFFF /* Direct */) { return false; }
|
||||
if(m.value == EFFECT_STATIC) { return false; }
|
||||
|
||||
uint32_t bit = 0u;
|
||||
switch(m.value)
|
||||
{
|
||||
case EFFECT_PULSE: bit = GB_EFF_BREATH; break;
|
||||
case EFFECT_COLORCYCLE: bit = GB_EFF_CYCLE; break;
|
||||
case EFFECT_BLINKING: bit = GB_EFF_FLASH; break;
|
||||
case EFFECT_RANDOM: bit = GB_EFF_RANDOM; break;
|
||||
case EFFECT_WAVE: bit = GB_EFF_WAVE; break;
|
||||
case EFFECT_DFLASH: bit = GB_EFF_DFLASH; break;
|
||||
case EFFECT_WAVE1: bit = GB_EFF_WAVE1; break;
|
||||
case EFFECT_WAVE2: bit = GB_EFF_WAVE2; break;
|
||||
case EFFECT_WAVE3: bit = GB_EFF_WAVE1; break;
|
||||
case EFFECT_WAVE4: bit = GB_EFF_WAVE2; break;
|
||||
default: bit = 0u; break;
|
||||
}
|
||||
return (bit == 0u) || ((effect_mask & bit) == 0u);
|
||||
}),
|
||||
modes.end());
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Iterate through layout and process each zone |
|
||||
\*---------------------------------------------------------*/
|
||||
for(uint8_t zone_idx = 0; zone_idx < GB_FUSION2_ZONES_MAX; zone_idx++)
|
||||
{
|
||||
if(!layout->zones[0][zone_idx])
|
||||
if(!(*instance_layout.zones)[zone_idx])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const gb_fusion2_zone* zone_at_idx = layout->zones[0][zone_idx];
|
||||
const gb_fusion2_zone* zone_at_idx = (*instance_layout.zones)[zone_idx];
|
||||
|
||||
zone new_zone;
|
||||
new_zone.name = zone_at_idx->name;
|
||||
new_zone.leds_min = zone_at_idx->leds_min;
|
||||
new_zone.leds_max = zone_at_idx->leds_max;
|
||||
new_zone.leds_count = new_zone.leds_min;
|
||||
new_zone.type = (new_zone.leds_min == new_zone.leds_max) ? ZONE_TYPE_SINGLE : ZONE_TYPE_LINEAR;
|
||||
new_zone.type = ((new_zone.leds_min == 1) && (new_zone.leds_max == 1)) ? ZONE_TYPE_SINGLE : ZONE_TYPE_LINEAR;
|
||||
new_zone.matrix_map = NULL;
|
||||
zones.emplace_back(new_zone);
|
||||
}
|
||||
@@ -363,17 +449,68 @@ void RGBController_RGBFusion2USB::SetupZones()
|
||||
| Set up zones (Fixed so as to not spam the controller) |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
std::vector<Gen2StripInfo> strips = controller->ExportGen2Strips();
|
||||
|
||||
for(uint8_t zone_idx = 0; zone_idx < GB_FUSION2_ZONES_MAX; zone_idx++)
|
||||
{
|
||||
const gb_fusion2_zone* zone_at_idx = gb_fusion2_device_list[device_index]->zones[0][zone_idx];
|
||||
const gb_fusion2_zone* zone_at_idx = (*instance_layout.zones)[zone_idx];
|
||||
if(!zone_at_idx)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool single_zone = (zone_at_idx->leds_min == zone_at_idx->leds_max);
|
||||
bool single_zone = ((zone_at_idx->leds_min == 1) && (zone_at_idx->leds_max == 1));
|
||||
|
||||
if(!single_zone)
|
||||
{
|
||||
if(supports_gen2)
|
||||
{
|
||||
int slot = 0;
|
||||
switch(zone_at_idx->idx)
|
||||
{
|
||||
case LED4:
|
||||
case HDR_D_LED2:
|
||||
slot = 1;
|
||||
break;
|
||||
case HDR_D_LED3:
|
||||
slot = 2;
|
||||
break;
|
||||
case HDR_D_LED4:
|
||||
slot = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(slot >= 0 && static_cast<size_t>(slot) < strips.size())
|
||||
{
|
||||
const Gen2StripInfo& info = strips[static_cast<size_t>(slot)];
|
||||
if(info.totalLeds > 0u)
|
||||
{
|
||||
zones[zone_idx].leds_count = static_cast<unsigned int>(info.totalLeds);
|
||||
zones[zone_idx].leds_min = static_cast<unsigned int>(info.totalLeds);
|
||||
zones[zone_idx].leds_max = static_cast<unsigned int>(info.totalLeds);
|
||||
zones[zone_idx].segments.clear();
|
||||
|
||||
zones[zone_idx].segments.reserve(info.LedsOfStrip.size());
|
||||
unsigned int offset = 0;
|
||||
for(size_t si = 0; si < info.LedsOfStrip.size(); ++si)
|
||||
{
|
||||
const uint16_t cnt = info.LedsOfStrip[si];
|
||||
if(cnt == 0) continue;
|
||||
|
||||
segment seg;
|
||||
seg.name = std::string("Segment ") + std::to_string(si);
|
||||
seg.type = ZONE_TYPE_LINEAR;
|
||||
seg.start_idx = offset;
|
||||
seg.leds_count = static_cast<unsigned int>(cnt);
|
||||
|
||||
zones[zone_idx].segments.push_back(seg);
|
||||
offset += static_cast<unsigned int>(cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(zone_at_idx->idx)
|
||||
{
|
||||
case LED4:
|
||||
@@ -826,6 +963,7 @@ void RGBController_RGBFusion2USB::LoadCustomLayoutFromJson(
|
||||
&& new_zone->idx <= GB_FUSION2_LED_IDX::LED11)
|
||||
{
|
||||
layout->zones[0][zone_idx] = new_zone;
|
||||
allocated_zones.push_back(new_zone);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -62,17 +62,26 @@ private:
|
||||
std::string detector_name;
|
||||
|
||||
RGBFusion2USBController* controller;
|
||||
int device_num;
|
||||
uint8_t device_num;
|
||||
RGBColor null_color = 0;
|
||||
bool supports_gen2 = 0;
|
||||
/*---------------------------------------------------------*\
|
||||
| The intial value of device_index should point to the |
|
||||
| layout for the generic_device |
|
||||
\*---------------------------------------------------------*/
|
||||
uint32_t device_index = 0;
|
||||
|
||||
uint16_t product_id = 0;
|
||||
uint32_t effects_mask = 0;
|
||||
void Init_Controller();
|
||||
int GetLED_Zone(int led_idx);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Per instance layout lookup tables. |
|
||||
\*---------------------------------------------------------*/
|
||||
gb_fusion2_device instance_layout{};
|
||||
gb_fusion2_layout instance_zones{};
|
||||
std::vector<gb_fusion2_zone*> allocated_zones;
|
||||
|
||||
nlohmann::json WriteCalJsonFrom(
|
||||
const EncodedCalibration& src);
|
||||
void FillMissingWith(
|
||||
|
||||
@@ -105,6 +105,7 @@ void DetectGigabyteRGBFusionGPUControllers(i2c_smbus_interface* bus, uint8_t i2c
|
||||
}
|
||||
} /* DetectGigabyteRGBFusionGPUControllers() */
|
||||
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GTX 1050 G1 Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1050_G1_GAMING_SUB_DEV, 0x47);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1050 Ti G1 Gaming Rev A1", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1050TI_G1_GAMING_SUB_DEV, 0x47);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1050 Ti G1 Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1050TI_G1_GAMING_SUB_DEV, 0x48);
|
||||
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1060 G1 Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1060_G1_GAMING_SUB_DEV, 0x48);
|
||||
|
||||
@@ -16,10 +16,21 @@
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static std::map<std::string, unsigned int> govee_led_counts
|
||||
struct GoveeHardwareInfo
|
||||
{
|
||||
{ "H619A", 20 },
|
||||
{ "H70B1", 20 },
|
||||
unsigned int led_count;
|
||||
unsigned int matrix_row_len; // 0 = linear
|
||||
};
|
||||
|
||||
const unsigned int GOVEE_FALLBACK_LED_COUNT = 20;
|
||||
|
||||
static std::map<std::string, GoveeHardwareInfo> govee_hardware_info
|
||||
{
|
||||
{ "H6022", { 132, 12 } }, // Govee Smart Table Lamp 2
|
||||
{ "H612F", { 12, 0 } }, // Govee Strip Light S (3m)
|
||||
{ "H619A", { 20, 0 } }, // Govee RGBIC Led Strip Lights
|
||||
{ "H70B1", { 20, 0 } }, // Govee LED Curtain Lights
|
||||
{ "H607C", { 174, 0 } }, // Govee Floor Lamp 2
|
||||
};
|
||||
|
||||
RGBController_Govee::RGBController_Govee(GoveeController* controller_ptr)
|
||||
@@ -67,39 +78,64 @@ RGBController_Govee::~RGBController_Govee()
|
||||
|
||||
void RGBController_Govee::SetupZones()
|
||||
{
|
||||
unsigned int led_count = 0;
|
||||
std::map<std::string, unsigned int>::iterator it = govee_led_counts.find(controller->GetSku());
|
||||
if(it != govee_led_counts.end())
|
||||
GoveeHardwareInfo hw = { GOVEE_FALLBACK_LED_COUNT, 0 };
|
||||
bool resizable = true;
|
||||
|
||||
std::map<std::string, GoveeHardwareInfo>::iterator it = govee_hardware_info.find(controller->GetSku());
|
||||
if(it != govee_hardware_info.end())
|
||||
{
|
||||
led_count = it->second;
|
||||
}
|
||||
/*-----------------------------------------------------*\
|
||||
| Fallback so Direct mode is usable even if SKU isn't |
|
||||
| in the table |
|
||||
\*-----------------------------------------------------*/
|
||||
if(led_count == 0)
|
||||
{
|
||||
led_count = 20; /* safe default; user can resize in UI */
|
||||
hw = it->second;
|
||||
resizable = false;
|
||||
}
|
||||
|
||||
zone strip;
|
||||
strip.name = "Govee Strip";
|
||||
strip.type = ZONE_TYPE_LINEAR;
|
||||
strip.leds_count = led_count;
|
||||
/*-----------------------------------------------------*\
|
||||
| Only make resizable for unknown SKUs |
|
||||
\*-----------------------------------------------------*/
|
||||
if(govee_led_counts.find(controller->GetSku()) == govee_led_counts.end())
|
||||
strip.leds_count = hw.led_count;
|
||||
strip.leds_min = resizable ? 0 : hw.led_count;
|
||||
strip.leds_max = resizable ? 255 : hw.led_count;
|
||||
|
||||
if(hw.matrix_row_len == 0)
|
||||
{
|
||||
strip.leds_min = 1;
|
||||
strip.leds_max = 255;
|
||||
strip.name = "Govee Strip";
|
||||
strip.type = ZONE_TYPE_LINEAR;
|
||||
strip.matrix_map = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
strip.leds_min = led_count;
|
||||
strip.leds_max = led_count;
|
||||
strip.name = "Govee Matrix";
|
||||
strip.type = ZONE_TYPE_MATRIX;
|
||||
|
||||
unsigned int width = hw.matrix_row_len;
|
||||
unsigned int height = hw.led_count / width;
|
||||
|
||||
strip.matrix_map = new matrix_map_type;
|
||||
strip.matrix_map->height = height;
|
||||
strip.matrix_map->width = width;
|
||||
strip.matrix_map->map = new unsigned int[hw.led_count];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| On H6022, LEDs indexed bottom to top, alternating |
|
||||
| clockwise and counterclockwise for each row. |
|
||||
\*-----------------------------------------------------*/
|
||||
for(unsigned int y = 0; y < height; y++)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| LEDs numbered bottom to top, opposite of matrix |
|
||||
| clockwise and counterclockwise for each row. |
|
||||
\*-------------------------------------------------*/
|
||||
unsigned int led_y = (height - 1) - y;
|
||||
|
||||
for(unsigned int x = 0; x < width; x++)
|
||||
{
|
||||
/*---------------------------------------------*\
|
||||
| LED is right-to-left for even rows, including |
|
||||
| first one |
|
||||
\*---------------------------------------------*/
|
||||
unsigned int led_x = led_y & 1 ? x : (width - 1) - x;
|
||||
strip.matrix_map->map[y * width + x] = led_y * width + led_x;
|
||||
}
|
||||
}
|
||||
}
|
||||
strip.matrix_map = NULL;
|
||||
|
||||
zones.push_back(strip);
|
||||
|
||||
for(std::size_t led_idx = 0; led_idx < strip.leds_count; led_idx++)
|
||||
@@ -114,6 +150,11 @@ void RGBController_Govee::SetupZones()
|
||||
|
||||
void RGBController_Govee::ResizeZone(int zone, int new_size)
|
||||
{
|
||||
if(zones[zone].type == ZONE_TYPE_MATRIX)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(zone < 0 || zone >= (int)zones.size() || new_size <= 0)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -30,28 +30,9 @@ using namespace std::chrono_literals;
|
||||
|
||||
bool TestForHyperXDRAMController(i2c_smbus_interface* bus, unsigned char address)
|
||||
{
|
||||
bool pass = false;
|
||||
int res = bus->i2c_smbus_read_byte(address);
|
||||
|
||||
int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE);
|
||||
|
||||
LOG_DEBUG("[%s] Writing at address %02X, res=%02X", HYPERX_CONTROLLER_NAME, address, res);
|
||||
|
||||
if (res >= 0)
|
||||
{
|
||||
pass = true;
|
||||
|
||||
for (int i = 0xA0; i < 0xB0; i++)
|
||||
{
|
||||
res = bus->i2c_smbus_read_byte_data(address, i);
|
||||
|
||||
if (res != i)
|
||||
{
|
||||
pass = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(pass);
|
||||
return(res >= 0);
|
||||
|
||||
} /* TestForHyperXDRAMController() */
|
||||
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| HyperXEve1800Controller.cpp |
|
||||
| |
|
||||
| Driver for HyperX Eve 1800 keyboard |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <cstring>
|
||||
#include "HyperXEve1800Controller.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
HyperXEve1800Controller::HyperXEve1800Controller(hid_device* dev_handle, const char* path, std::string dev_name)
|
||||
{
|
||||
dev = dev_handle;
|
||||
location = path;
|
||||
name = dev_name;
|
||||
}
|
||||
|
||||
HyperXEve1800Controller::~HyperXEve1800Controller()
|
||||
{
|
||||
hid_close(dev);
|
||||
}
|
||||
|
||||
std::string HyperXEve1800Controller::GetDeviceLocation()
|
||||
{
|
||||
return("HID: " + location);
|
||||
}
|
||||
|
||||
std::string HyperXEve1800Controller::GetNameString()
|
||||
{
|
||||
return(name);
|
||||
}
|
||||
|
||||
std::string HyperXEve1800Controller::GetSerialString()
|
||||
{
|
||||
wchar_t serial_string[128];
|
||||
int ret = hid_get_serial_number_string(dev, serial_string, 128);
|
||||
|
||||
if(ret != 0)
|
||||
{
|
||||
return("");
|
||||
}
|
||||
|
||||
return(StringUtils::wstring_to_string(serial_string));
|
||||
}
|
||||
|
||||
void HyperXEve1800Controller::SetBrightness(unsigned int brightness)
|
||||
{
|
||||
unsigned char buf[65];
|
||||
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
|
||||
buf[0x00] = 0x40;
|
||||
buf[0x01] = 0x01;
|
||||
buf[0x02] = 0x00;
|
||||
buf[0x03] = 0x00;
|
||||
buf[0x04] = brightness & 0xFF;
|
||||
|
||||
hid_write(dev, buf, 65);
|
||||
}
|
||||
|
||||
void HyperXEve1800Controller::SetLEDsDirect(std::vector<RGBColor> colors)
|
||||
{
|
||||
SendDirectInitialization();
|
||||
|
||||
if(colors.empty())
|
||||
{
|
||||
RGBColor temp = 0x00000000;
|
||||
SendDirectColorPacket(&temp, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SendDirectColorPacket(&colors[0], (unsigned int)colors.size());
|
||||
}
|
||||
}
|
||||
|
||||
void HyperXEve1800Controller::SendDirectInitialization()
|
||||
{
|
||||
unsigned char buf[65];
|
||||
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
|
||||
buf[0x00] = 0x44;
|
||||
buf[0x01] = 0x01;
|
||||
buf[0x02] = 0x04;
|
||||
buf[0x03] = 0x00;
|
||||
|
||||
hid_write(dev, buf, 65);
|
||||
}
|
||||
|
||||
void HyperXEve1800Controller::SendDirectColorPacket(RGBColor* color_data, unsigned int color_count)
|
||||
{
|
||||
unsigned char buf[65];
|
||||
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
|
||||
buf[0x00] = 0x44;
|
||||
buf[0x01] = 0x02;
|
||||
buf[0x02] = 0x00;
|
||||
buf[0x03] = 0x00;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| The Eve 1800 exposes 10 lighting zones, but this |
|
||||
| report format can carry up to 20 RGB triplets. |
|
||||
\*-----------------------------------------------------*/
|
||||
if(color_count > 20)
|
||||
{
|
||||
color_count = 20;
|
||||
}
|
||||
|
||||
for(unsigned int color_idx = 0; color_idx < color_count; color_idx++)
|
||||
{
|
||||
buf[4 + (color_idx * 3)] = RGBGetRValue(color_data[color_idx]);
|
||||
buf[4 + (color_idx * 3) + 1] = RGBGetGValue(color_data[color_idx]);
|
||||
buf[4 + (color_idx * 3) + 2] = RGBGetBValue(color_data[color_idx]);
|
||||
}
|
||||
|
||||
hid_write(dev, buf, 65);
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| HyperXEve1800Controller.h |
|
||||
| |
|
||||
| Driver for HyperX Eve 1800 keyboard |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <hidapi.h>
|
||||
#include "RGBController.h"
|
||||
|
||||
class HyperXEve1800Controller
|
||||
{
|
||||
public:
|
||||
HyperXEve1800Controller(hid_device* dev_handle, const char* path, std::string dev_name);
|
||||
~HyperXEve1800Controller();
|
||||
|
||||
std::string GetDeviceLocation();
|
||||
std::string GetNameString();
|
||||
std::string GetSerialString();
|
||||
|
||||
void SetBrightness(unsigned int brightness);
|
||||
void SetLEDsDirect(std::vector<RGBColor> colors);
|
||||
|
||||
private:
|
||||
hid_device* dev;
|
||||
std::string location;
|
||||
std::string name;
|
||||
|
||||
void SendDirectInitialization();
|
||||
void SendDirectColorPacket(RGBColor* color_data, unsigned int color_count);
|
||||
};
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_HyperXEve1800.cpp |
|
||||
| |
|
||||
| RGBController for HyperX Eve 1800 keyboard |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "RGBController_HyperXEve1800.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
#define HYPERX_EVE_1800_BRIGHTNESS_MIN 0x00
|
||||
#define HYPERX_EVE_1800_BRIGHTNESS_MAX 0xFF
|
||||
#define HYPERX_EVE_1800_ZONE_COUNT 10
|
||||
|
||||
/**------------------------------------------------------------------*\
|
||||
@name HyperX Eve 1800
|
||||
@category Keyboard
|
||||
@type USB
|
||||
@save :x:
|
||||
@direct :white_check_mark:
|
||||
@effects :x:
|
||||
@detectors DetectHyperXEve1800
|
||||
@comment
|
||||
\*-------------------------------------------------------------------*/
|
||||
|
||||
RGBController_HyperXEve1800::RGBController_HyperXEve1800(HyperXEve1800Controller* controller_ptr)
|
||||
{
|
||||
controller = controller_ptr;
|
||||
|
||||
name = controller->GetNameString();
|
||||
vendor = "HyperX";
|
||||
type = DEVICE_TYPE_KEYBOARD;
|
||||
description = "HyperX Eve 1800 Keyboard Device";
|
||||
location = controller->GetDeviceLocation();
|
||||
serial = controller->GetSerialString();
|
||||
|
||||
mode Direct;
|
||||
Direct.name = "Direct";
|
||||
Direct.value = 0xFFFF;
|
||||
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
|
||||
Direct.brightness_min = HYPERX_EVE_1800_BRIGHTNESS_MIN;
|
||||
Direct.brightness_max = HYPERX_EVE_1800_BRIGHTNESS_MAX;
|
||||
Direct.brightness = HYPERX_EVE_1800_BRIGHTNESS_MAX;
|
||||
Direct.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Direct);
|
||||
|
||||
SetupZones();
|
||||
|
||||
keepalive_thread_run = 1;
|
||||
keepalive_thread = new std::thread(&RGBController_HyperXEve1800::KeepaliveThread, this);
|
||||
}
|
||||
|
||||
RGBController_HyperXEve1800::~RGBController_HyperXEve1800()
|
||||
{
|
||||
keepalive_thread_run = 0;
|
||||
keepalive_thread->join();
|
||||
delete keepalive_thread;
|
||||
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void RGBController_HyperXEve1800::SetupZones()
|
||||
{
|
||||
zone new_zone;
|
||||
new_zone.name = "Keyboard";
|
||||
new_zone.type = ZONE_TYPE_LINEAR;
|
||||
new_zone.leds_min = HYPERX_EVE_1800_ZONE_COUNT;
|
||||
new_zone.leds_max = HYPERX_EVE_1800_ZONE_COUNT;
|
||||
new_zone.leds_count = HYPERX_EVE_1800_ZONE_COUNT;
|
||||
new_zone.matrix_map = NULL;
|
||||
zones.push_back(new_zone);
|
||||
|
||||
for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++)
|
||||
{
|
||||
led new_led;
|
||||
new_led.name = "Zone ";
|
||||
new_led.name.append(std::to_string(led_idx + 1));
|
||||
leds.push_back(new_led);
|
||||
}
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
void RGBController_HyperXEve1800::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RGBController_HyperXEve1800::DeviceUpdateLEDs()
|
||||
{
|
||||
controller->SetLEDsDirect(colors);
|
||||
last_update_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void RGBController_HyperXEve1800::UpdateZoneLEDs(int /*zone*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_HyperXEve1800::UpdateSingleLED(int /*led*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_HyperXEve1800::DeviceUpdateMode()
|
||||
{
|
||||
controller->SetBrightness(modes[active_mode].brightness);
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_HyperXEve1800::KeepaliveThread()
|
||||
{
|
||||
while(keepalive_thread_run.load())
|
||||
{
|
||||
if(active_mode == 0)
|
||||
{
|
||||
if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50))
|
||||
{
|
||||
UpdateLEDs();
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_HyperXEve1800.h |
|
||||
| |
|
||||
| RGBController for HyperX Eve 1800 keyboard |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include "RGBController.h"
|
||||
#include "HyperXEve1800Controller.h"
|
||||
|
||||
class RGBController_HyperXEve1800 : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_HyperXEve1800(HyperXEve1800Controller* controller_ptr);
|
||||
~RGBController_HyperXEve1800();
|
||||
|
||||
void SetupZones();
|
||||
|
||||
void ResizeZone(int zone, int new_size);
|
||||
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
|
||||
void DeviceUpdateMode();
|
||||
|
||||
void KeepaliveThread();
|
||||
|
||||
private:
|
||||
HyperXEve1800Controller* controller;
|
||||
std::thread* keepalive_thread;
|
||||
std::atomic<bool> keepalive_thread_run;
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
|
||||
};
|
||||
@@ -15,12 +15,16 @@
|
||||
#include "HyperXAlloyOriginsController.h"
|
||||
#include "HyperXAlloyOriginsCoreController.h"
|
||||
#include "HyperXAlloyOrigins60and65Controller.h"
|
||||
#include "HyperXEve1800Controller.h"
|
||||
#include "HyperXOrigins2_65Controller.h"
|
||||
#include "RGBController_HyperXAlloyElite.h"
|
||||
#include "RGBController_HyperXAlloyElite2.h"
|
||||
#include "RGBController_HyperXAlloyFPS.h"
|
||||
#include "RGBController_HyperXAlloyOrigins.h"
|
||||
#include "RGBController_HyperXAlloyOriginsCore.h"
|
||||
#include "RGBController_HyperXAlloyOrigins60and65.h"
|
||||
#include "RGBController_HyperXEve1800.h"
|
||||
#include "RGBController_HyperXOrigins2_65.h"
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| HyperX keyboard vendor and product IDs |
|
||||
@@ -44,6 +48,8 @@
|
||||
#define HYPERX_ALLOY_ORIGINS_65_HP_PID 0x038F
|
||||
#define HYPERX_ALLOY_ORIGINS_CORE_HP_PID 0x098F
|
||||
#define HYPERX_ALLOY_ORIGINS_HP_PID 0x0591
|
||||
#define HYPERX_EVE_1800_HP_PID 0x08C2
|
||||
#define HYPERX_ORIGINS_2_65_HP_PID 0x0CC2
|
||||
|
||||
AlloyOrigins60and65MappingLayoutType GetAlloyOrigins60and65MappingLayoutType(int pid)
|
||||
{
|
||||
@@ -140,12 +146,41 @@ void DetectHyperXAlloyOrigins60and65(hid_device_info* info, const std::string& n
|
||||
}
|
||||
}
|
||||
|
||||
void DetectHyperXOrigins2_65(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(dev)
|
||||
{
|
||||
HyperXOrigins2_65Controller* controller = new HyperXOrigins2_65Controller(dev, info->path, name);
|
||||
RGBController_HyperXOrigins2_65* rgb_controller = new RGBController_HyperXOrigins2_65(controller);
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
|
||||
void DetectHyperXEve1800(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(dev)
|
||||
{
|
||||
HyperXEve1800Controller* controller = new HyperXEve1800Controller(dev, info->path, name);
|
||||
RGBController_HyperXEve1800* rgb_controller = new RGBController_HyperXEve1800(controller);
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite RGB", DetectHyperXAlloyElite, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_PID, 2, 0xFF01);
|
||||
REGISTER_HID_DETECTOR_IP("HyperX Alloy FPS RGB", DetectHyperXAlloyFPS, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_FPS_RGB_PID, 2, 0xFF01);
|
||||
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core", DetectHyperXAlloyOriginsCore, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_PID, 2);
|
||||
|
||||
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core (HP)", DetectHyperXAlloyOriginsCore, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_HP_PID, 2);
|
||||
|
||||
REGISTER_HID_DETECTOR_I("HyperX Origins 2 65 (HP)", DetectHyperXOrigins2_65, HP_KEYBOARD_VID, HYPERX_ORIGINS_2_65_HP_PID, 3);
|
||||
REGISTER_HID_DETECTOR_I("HyperX Eve 1800 (HP)", DetectHyperXEve1800, HP_KEYBOARD_VID, HYPERX_EVE_1800_HP_PID, 2);
|
||||
|
||||
#ifdef _WIN32
|
||||
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 3);
|
||||
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite 2", DetectHyperXAlloyElite2, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_PID, 3, 0xFF90);
|
||||
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| HyperXOrigins2_65Controller.cpp |
|
||||
| |
|
||||
| Driver for HyperX Origins 2 65 keyboard |
|
||||
| |
|
||||
| Ricardo Amorim 28 Mar 2026 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <cstring>
|
||||
#include "HyperXOrigins2_65Controller.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
HyperXOrigins2_65Controller::HyperXOrigins2_65Controller(hid_device* dev_handle, const char* path, std::string dev_name)
|
||||
{
|
||||
dev = dev_handle;
|
||||
location = path;
|
||||
name = dev_name;
|
||||
}
|
||||
|
||||
HyperXOrigins2_65Controller::~HyperXOrigins2_65Controller()
|
||||
{
|
||||
hid_close(dev);
|
||||
}
|
||||
|
||||
std::string HyperXOrigins2_65Controller::GetDeviceLocation()
|
||||
{
|
||||
return("HID " + location);
|
||||
}
|
||||
|
||||
std::string HyperXOrigins2_65Controller::GetNameString()
|
||||
{
|
||||
return(name);
|
||||
}
|
||||
|
||||
std::string HyperXOrigins2_65Controller::GetSerialString()
|
||||
{
|
||||
wchar_t serial_string[128];
|
||||
int ret = hid_get_serial_number_string(dev, serial_string, 128);
|
||||
|
||||
if(ret != 0)
|
||||
{
|
||||
return ("");
|
||||
}
|
||||
|
||||
return(StringUtils::wstring_to_string(serial_string));
|
||||
}
|
||||
|
||||
void HyperXOrigins2_65Controller::SetLEDsDirect(std::vector<RGBColor> colors)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up variables to track progress of color transmit |
|
||||
| Do this after inserting blanks |
|
||||
\*-----------------------------------------------------*/
|
||||
int colors_to_send = (int)colors.size();
|
||||
int colors_sent = 0;
|
||||
|
||||
SendDirectInitialization();
|
||||
|
||||
for(int pkt_idx = 0; pkt_idx < 4; pkt_idx++)
|
||||
{
|
||||
if(colors_to_send > 20)
|
||||
{
|
||||
SendDirectColorPacket(&colors[colors_sent], 20, pkt_idx);
|
||||
colors_sent += 20;
|
||||
colors_to_send -= 20;
|
||||
}
|
||||
else if(colors_to_send > 0)
|
||||
{
|
||||
SendDirectColorPacket(&colors[colors_sent], colors_to_send, pkt_idx);
|
||||
colors_sent += colors_to_send;
|
||||
colors_to_send -= colors_to_send;
|
||||
}
|
||||
else
|
||||
{
|
||||
RGBColor temp = 0x00000000;
|
||||
SendDirectColorPacket(&temp, 1, pkt_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HyperXOrigins2_65Controller::SendDirectInitialization()
|
||||
{
|
||||
unsigned char buf[65];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up Direct Initialization packet |
|
||||
\*-----------------------------------------------------*/
|
||||
buf[0x00] = 0x44;
|
||||
buf[0x01] = 0x01;
|
||||
buf[0x02] = 0x04;
|
||||
buf[0x03] = 0x00;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev, buf, 65);
|
||||
}
|
||||
|
||||
void HyperXOrigins2_65Controller::SendDirectColorPacket
|
||||
(
|
||||
RGBColor* color_data,
|
||||
unsigned int color_count,
|
||||
unsigned int seq
|
||||
)
|
||||
{
|
||||
unsigned char buf[65];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set up Direct Initialization packet |
|
||||
\*-----------------------------------------------------*/
|
||||
buf[0x00] = 0x44;
|
||||
buf[0x01] = 0x02;
|
||||
buf[0x02] = (unsigned char)seq;
|
||||
buf[0x03] = 0x00;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| The maximum number of colors per packet is 20 |
|
||||
\*-----------------------------------------------------*/
|
||||
if(color_count > 20)
|
||||
{
|
||||
color_count = 20;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Copy in color data |
|
||||
\*-----------------------------------------------------*/
|
||||
for(unsigned int color_idx = 0; color_idx < color_count; color_idx++)
|
||||
{
|
||||
buf[4 + (color_idx * 3)] = RGBGetRValue(color_data[color_idx]);
|
||||
buf[4 + (color_idx * 3) + 1] = RGBGetGValue(color_data[color_idx]);
|
||||
buf[4 + (color_idx * 3) + 2] = RGBGetBValue(color_data[color_idx]);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev, buf, 65);
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| HyperXOrigins2_65Controller.h |
|
||||
| |
|
||||
| Driver for HyperX Origins 2 65 keyboard |
|
||||
| |
|
||||
| Ricardo Amorim 28 Mar 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 "RGBController.h"
|
||||
|
||||
class HyperXOrigins2_65Controller
|
||||
{
|
||||
public:
|
||||
HyperXOrigins2_65Controller(hid_device* dev_handle, const char* path, std::string dev_name);
|
||||
~HyperXOrigins2_65Controller();
|
||||
|
||||
std::string GetDeviceLocation();
|
||||
std::string GetNameString();
|
||||
std::string GetSerialString();
|
||||
|
||||
void SetLEDsDirect(std::vector<RGBColor> colors);
|
||||
|
||||
private:
|
||||
hid_device* dev;
|
||||
std::string location;
|
||||
std::string name;
|
||||
|
||||
void SendDirectInitialization();
|
||||
void SendDirectColorPacket
|
||||
(
|
||||
RGBColor* color_data,
|
||||
unsigned int color_count,
|
||||
unsigned int seq
|
||||
);
|
||||
};
|
||||
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_HyperXOrigins2_65.cpp |
|
||||
| |
|
||||
| RGBController for HyperX Origins 2 65 keyboard |
|
||||
| |
|
||||
| Ricardo Amorim 28 Mar 2026 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "RGBControllerKeyNames.h"
|
||||
#include "RGBController_HyperXOrigins2_65.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
//0xFFFFFFFF indicates an unused entry in matrix
|
||||
#define NA 0xFFFFFFFF
|
||||
|
||||
static unsigned int matrix_map[5][15] =
|
||||
{ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 },
|
||||
{ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, NA, 29 },
|
||||
{ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44 },
|
||||
{ 45, 61, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 59 },
|
||||
{ 60, 62, 63, 65, NA, NA, 66, NA, NA, 68, 69, 70, 71, 72, 73 } };
|
||||
|
||||
static const char* zone_names[] =
|
||||
{
|
||||
ZONE_EN_KEYBOARD,
|
||||
};
|
||||
|
||||
static zone_type zone_types[] =
|
||||
{
|
||||
ZONE_TYPE_MATRIX,
|
||||
};
|
||||
|
||||
static const unsigned int zone_sizes[] =
|
||||
{
|
||||
74,
|
||||
};
|
||||
|
||||
static const char *led_names[] =
|
||||
{
|
||||
KEY_EN_ESCAPE,
|
||||
KEY_EN_1,
|
||||
KEY_EN_2,
|
||||
KEY_EN_3,
|
||||
KEY_EN_4,
|
||||
KEY_EN_5,
|
||||
KEY_EN_6,
|
||||
KEY_EN_7,
|
||||
KEY_EN_8,
|
||||
KEY_EN_9,
|
||||
KEY_EN_0,
|
||||
KEY_EN_MINUS,
|
||||
KEY_EN_EQUALS,
|
||||
KEY_EN_BACKSPACE,
|
||||
KEY_EN_DELETE,
|
||||
KEY_EN_TAB,
|
||||
KEY_EN_Q,
|
||||
KEY_EN_W,
|
||||
KEY_EN_E,
|
||||
KEY_EN_R,
|
||||
KEY_EN_T,
|
||||
KEY_EN_Y,
|
||||
KEY_EN_U,
|
||||
KEY_EN_I,
|
||||
KEY_EN_O,
|
||||
KEY_EN_P,
|
||||
KEY_EN_LEFT_BRACKET,
|
||||
KEY_EN_RIGHT_BRACKET,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_HOME,
|
||||
KEY_EN_CAPS_LOCK,
|
||||
KEY_EN_A,
|
||||
KEY_EN_S,
|
||||
KEY_EN_D,
|
||||
KEY_EN_F,
|
||||
KEY_EN_G,
|
||||
KEY_EN_H,
|
||||
KEY_EN_J,
|
||||
KEY_EN_K,
|
||||
KEY_EN_L,
|
||||
KEY_EN_SEMICOLON,
|
||||
KEY_EN_QUOTE,
|
||||
KEY_EN_POUND,
|
||||
KEY_EN_ISO_ENTER,
|
||||
KEY_EN_PAGE_UP,
|
||||
KEY_EN_LEFT_SHIFT,
|
||||
KEY_EN_Z,
|
||||
KEY_EN_X,
|
||||
KEY_EN_C,
|
||||
KEY_EN_V,
|
||||
KEY_EN_B,
|
||||
KEY_EN_N,
|
||||
KEY_EN_M,
|
||||
KEY_EN_COMMA,
|
||||
KEY_EN_PERIOD,
|
||||
KEY_EN_FORWARD_SLASH,
|
||||
KEY_EN_RIGHT_SHIFT,
|
||||
KEY_EN_UP_ARROW,
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_PAGE_DOWN,
|
||||
KEY_EN_LEFT_CONTROL,
|
||||
KEY_EN_ISO_BACK_SLASH,
|
||||
KEY_EN_LEFT_WINDOWS,
|
||||
KEY_EN_LEFT_ALT,
|
||||
KEY_EN_UNUSED,
|
||||
"Left Space",
|
||||
KEY_EN_SPACE,
|
||||
"Right Space",
|
||||
KEY_EN_UNUSED,
|
||||
KEY_EN_RIGHT_ALT,
|
||||
KEY_EN_RIGHT_FUNCTION,
|
||||
KEY_EN_LEFT_ARROW,
|
||||
KEY_EN_DOWN_ARROW,
|
||||
KEY_EN_RIGHT_ARROW
|
||||
};
|
||||
|
||||
/**------------------------------------------------------------------*\
|
||||
@name HyperX Origins 2 65
|
||||
@category Keyboard
|
||||
@type USB
|
||||
@save :x:
|
||||
@direct :white_check_mark:
|
||||
@effects :x:
|
||||
@detectors DetectHyperXOrigins2_65
|
||||
@comment
|
||||
\*-------------------------------------------------------------------*/
|
||||
|
||||
RGBController_HyperXOrigins2_65::RGBController_HyperXOrigins2_65(HyperXOrigins2_65Controller* controller_ptr)
|
||||
{
|
||||
controller = controller_ptr;
|
||||
|
||||
name = controller->GetNameString();
|
||||
vendor = "HyperX";
|
||||
type = DEVICE_TYPE_KEYBOARD;
|
||||
location = controller->GetDeviceLocation();
|
||||
serial = controller->GetSerialString();
|
||||
|
||||
mode Direct;
|
||||
Direct.name = "Direct";
|
||||
Direct.value = 0xFFFF;
|
||||
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
|
||||
Direct.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Direct);
|
||||
|
||||
SetupZones();
|
||||
|
||||
keepalive_thread_run = 1;
|
||||
keepalive_thread = new std::thread(&RGBController_HyperXOrigins2_65::KeepaliveThread, this);
|
||||
}
|
||||
|
||||
RGBController_HyperXOrigins2_65::~RGBController_HyperXOrigins2_65()
|
||||
{
|
||||
keepalive_thread_run = 0;
|
||||
keepalive_thread->join();
|
||||
delete keepalive_thread;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Delete the matrix map |
|
||||
\*---------------------------------------------------------*/
|
||||
for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++)
|
||||
{
|
||||
if(zones[zone_index].matrix_map != NULL)
|
||||
{
|
||||
delete zones[zone_index].matrix_map;
|
||||
}
|
||||
}
|
||||
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void RGBController_HyperXOrigins2_65::SetupZones()
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Set up zones |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned int total_led_count = 0;
|
||||
for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++)
|
||||
{
|
||||
zone new_zone;
|
||||
new_zone.name = zone_names[zone_idx];
|
||||
new_zone.type = zone_types[zone_idx];
|
||||
new_zone.leds_min = zone_sizes[zone_idx];
|
||||
new_zone.leds_max = zone_sizes[zone_idx];
|
||||
new_zone.leds_count = zone_sizes[zone_idx];
|
||||
|
||||
if(zone_types[zone_idx] == ZONE_TYPE_MATRIX)
|
||||
{
|
||||
new_zone.matrix_map = new matrix_map_type;
|
||||
new_zone.matrix_map->height = 5;
|
||||
new_zone.matrix_map->width = 15;
|
||||
new_zone.matrix_map->map = (unsigned int *)&matrix_map;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_zone.matrix_map = NULL;
|
||||
}
|
||||
|
||||
zones.push_back(new_zone);
|
||||
|
||||
total_led_count += zone_sizes[zone_idx];
|
||||
}
|
||||
|
||||
for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++)
|
||||
{
|
||||
led new_led;
|
||||
new_led.name = led_names[led_idx];
|
||||
leds.push_back(new_led);
|
||||
}
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
void RGBController_HyperXOrigins2_65::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| This device does not support resizing zones |
|
||||
\*---------------------------------------------------------*/
|
||||
}
|
||||
|
||||
void RGBController_HyperXOrigins2_65::DeviceUpdateLEDs()
|
||||
{
|
||||
controller->SetLEDsDirect(colors);
|
||||
}
|
||||
|
||||
void RGBController_HyperXOrigins2_65::UpdateZoneLEDs(int /*zone*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_HyperXOrigins2_65::UpdateSingleLED(int /*led*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_HyperXOrigins2_65::DeviceUpdateMode()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RGBController_HyperXOrigins2_65::KeepaliveThread()
|
||||
{
|
||||
while(keepalive_thread_run.load())
|
||||
{
|
||||
if(active_mode == 0)
|
||||
{
|
||||
if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50))
|
||||
{
|
||||
UpdateLEDs();
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(10ms);;
|
||||
}
|
||||
}
|
||||
|
||||
+42
-43
@@ -1,43 +1,42 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_CorsairWireless.h |
|
||||
| |
|
||||
| RGBController for Corsair wireless keyboard |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 08 May 2021 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RGBController.h"
|
||||
#include "CorsairWirelessController.h"
|
||||
|
||||
class RGBController_CorsairWireless : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_CorsairWireless(CorsairWirelessController* controller_ptr);
|
||||
~RGBController_CorsairWireless();
|
||||
|
||||
int physical_layout;
|
||||
int logical_layout;
|
||||
|
||||
void SetupZones();
|
||||
|
||||
void ResizeZone(int zone, int new_size);
|
||||
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
|
||||
void DeviceUpdateMode();
|
||||
|
||||
void KeepaliveThread();
|
||||
|
||||
private:
|
||||
CorsairWirelessController* controller;
|
||||
std::thread* keepalive_thread;
|
||||
std::atomic<bool> keepalive_thread_run;
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
|
||||
};
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_HyperXOrigins2_65.h |
|
||||
| |
|
||||
| RGBController for HyperX Origins 2 65 keyboard |
|
||||
| |
|
||||
| Ricardo Amorim 28 Mar 2026 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include "RGBController.h"
|
||||
#include "HyperXOrigins2_65Controller.h"
|
||||
|
||||
class RGBController_HyperXOrigins2_65 : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_HyperXOrigins2_65(HyperXOrigins2_65Controller* controller_ptr);
|
||||
~RGBController_HyperXOrigins2_65();
|
||||
|
||||
void SetupZones();
|
||||
|
||||
void ResizeZone(int zone, int new_size);
|
||||
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
|
||||
void DeviceUpdateMode();
|
||||
|
||||
void KeepaliveThread();
|
||||
|
||||
private:
|
||||
HyperXOrigins2_65Controller* controller;
|
||||
std::thread* keepalive_thread;
|
||||
std::atomic<bool> keepalive_thread_run;
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
|
||||
};
|
||||
|
||||
@@ -118,7 +118,7 @@ void RGBController_IntelArcA770LE::SetupZones()
|
||||
for(unsigned int led_idx = 0; led_idx < 16; led_idx++)
|
||||
{
|
||||
led fan_1_led;
|
||||
fan_1_led.name = "Fan 1 LED";
|
||||
fan_1_led.name = "Fan 1 LED " + std::to_string(led_idx + 1);
|
||||
fan_1_led.value = fan_1_leds[led_idx];
|
||||
leds.push_back(fan_1_led);
|
||||
}
|
||||
@@ -126,7 +126,7 @@ void RGBController_IntelArcA770LE::SetupZones()
|
||||
for(unsigned int led_idx = 0; led_idx < 16; led_idx++)
|
||||
{
|
||||
led fan_2_led;
|
||||
fan_2_led.name = "Fan 2 LED";
|
||||
fan_2_led.name = "Fan 2 LED " + std::to_string(led_idx + 1);
|
||||
fan_2_led.value = fan_2_leds[led_idx];
|
||||
leds.push_back(fan_2_led);
|
||||
}
|
||||
@@ -134,7 +134,7 @@ void RGBController_IntelArcA770LE::SetupZones()
|
||||
for(unsigned int led_idx = 0; led_idx < 8; led_idx++)
|
||||
{
|
||||
led back_led;
|
||||
back_led.name = "Back LED";
|
||||
back_led.name = "Back LED " + std::to_string(led_idx + 1);
|
||||
back_led.value = back_leds[led_idx];
|
||||
leds.push_back(back_led);
|
||||
}
|
||||
@@ -142,7 +142,7 @@ void RGBController_IntelArcA770LE::SetupZones()
|
||||
for(unsigned int led_idx = 0; led_idx < 50; led_idx++)
|
||||
{
|
||||
led ring_led;
|
||||
ring_led.name = "Ring LED";
|
||||
ring_led.name = "Ring LED " + std::to_string(led_idx + 1);
|
||||
ring_led.value = ring_leds[led_idx];
|
||||
leds.push_back(ring_led);
|
||||
}
|
||||
@@ -150,7 +150,7 @@ void RGBController_IntelArcA770LE::SetupZones()
|
||||
for(unsigned int led_idx = 0; led_idx < 1; led_idx++)
|
||||
{
|
||||
led logo_led;
|
||||
logo_led.name = "Logo LED";
|
||||
logo_led.name = "Logo LED " + std::to_string(led_idx + 1);
|
||||
logo_led.value = logo_leds[led_idx];
|
||||
leds.push_back(logo_led);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <thread>
|
||||
#include <hidapi.h>
|
||||
#include "Detector.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "LogManager.h"
|
||||
#include "LogitechProtocolCommon.h"
|
||||
#include "LogitechG203LController.h"
|
||||
@@ -40,6 +41,8 @@
|
||||
#include "RGBController_LogitechLightspeed.h"
|
||||
#include "RGBController_LogitechGPowerPlay.h" // Linux-only
|
||||
#include "RGBController_LogitechX56.h"
|
||||
#include "LogitechHIDPP20Controller.h"
|
||||
#include "RGBController_LogitechHIDPP20.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -84,6 +87,9 @@ using namespace std::chrono_literals;
|
||||
#define LOGITECH_G502_HERO_PID 0xC08B
|
||||
#define LOGITECH_G502_LIGHTSPEED_PID 0xC08D
|
||||
#define LOGITECH_G502_X_PLUS_PID 0xC095
|
||||
#define LOGITECH_G515_LS_TKL_PID 0xC355
|
||||
#define LOGITECH_G522_LIGHTSPEED_USB_PID 0x0B19
|
||||
#define LOGITECH_G522_LIGHTSPEED_DONGLE_PID 0x0B18
|
||||
#define LOGITECH_G600_PID 0xC24A
|
||||
#define LOGITECH_G703_LIGHTSPEED_PID 0xC087
|
||||
#define LOGITECH_G703_HERO_LIGHTSPEED_PID 0xC090
|
||||
@@ -135,6 +141,8 @@ using namespace std::chrono_literals;
|
||||
#define LOGITECH_G903_LIGHTSPEED_VIRTUAL_HERO_PID 0x4087
|
||||
#define LOGITECH_G_PRO_WIRELESS_VIRTUAL_PID 0x4079
|
||||
#define LOGITECH_POWERPLAY_MAT_VIRTUAL_PID 0x405F
|
||||
#define LOGITECH_G502_X_PLUS_LIGHTSPEED_VIRTUAL_PID 0x4099
|
||||
#define LOGITECH_G515_LS_TKL_LIGHTSPEED_VIRTUAL_PID 0x40B4
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Joystick product IDs |
|
||||
@@ -358,6 +366,106 @@ void DetectLogitechKeyboardG915(hid_device_info* info, const std::string& name)
|
||||
}
|
||||
}
|
||||
|
||||
static bool ProbeG915ReceiverName(hid_device* dev, std::string& out_name)
|
||||
{
|
||||
/*---------------------------------------------------------*\n | HID++ short message name probe. |
|
||||
| Request: 10 01 03 0E 00 00 00 (get name length) |
|
||||
| Request: 10 01 03 1E 00 00 00 (get name string) |
|
||||
| Response: 11 01 03 1E <name bytes...> |
|
||||
| Verified against G915 TKL (PID 0xC547) which returns |
|
||||
| "G915 TKL LIGHTSP" (truncated, full: G915 TKL LIGHTSPEED) |
|
||||
\*---------------------------------------------------------*/
|
||||
const unsigned char req_len[7] = { 0x10, 0x01, 0x03, 0x0E, 0x00, 0x00, 0x00 };
|
||||
const unsigned char req_name[7] = { 0x10, 0x01, 0x03, 0x1E, 0x00, 0x00, 0x00 };
|
||||
unsigned char resp[64] = { 0 };
|
||||
|
||||
hid_write(dev, req_len, sizeof(req_len));
|
||||
hid_read_timeout(dev, resp, sizeof(resp), 100);
|
||||
|
||||
hid_write(dev, req_name, sizeof(req_name));
|
||||
for(int attempt = 0; attempt < 3; attempt++)
|
||||
{
|
||||
int rd = hid_read_timeout(dev, resp, sizeof(resp), 200);
|
||||
if(rd < 8)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(resp[0] == 0x11 && resp[1] == 0x01 && resp[2] == 0x03 && resp[3] == 0x1E)
|
||||
{
|
||||
std::string name_str;
|
||||
for(int i = 4; i < rd; i++)
|
||||
{
|
||||
if(resp[i] == 0x00)
|
||||
{
|
||||
break;
|
||||
}
|
||||
name_str.push_back(static_cast<char>(resp[i]));
|
||||
}
|
||||
out_name = name_str;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DetectLogitechKeyboardG915Receiver2(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| PID 0xC547 is shared by multiple Logitech keyboards. |
|
||||
| Use a HID++ name probe to identify the exact device and |
|
||||
| route to the correct controller. |
|
||||
| |
|
||||
| Known devices behind this PID: |
|
||||
| "G915 TKL LIGHTSP..." -> G915 TKL (is_tkl = true) |
|
||||
| "G915 LIGHTSP..." -> G915 full-size (is_tkl = false)|
|
||||
| "G515..." -> G515 (not handled here) |
|
||||
\*---------------------------------------------------------*/
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
if(!dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string probed_name;
|
||||
bool ok = ProbeG915ReceiverName(dev, probed_name);
|
||||
|
||||
if(!ok)
|
||||
{
|
||||
LOG_DEBUG("[LogitechControllerDetect] 0xC547 name probe failed, skipping device");
|
||||
hid_close(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("[LogitechControllerDetect] 0xC547 probe returned name=\"%s\"", probed_name.c_str());
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Route based on probed name. Check for TKL before full |
|
||||
| G915 since both contain "G915". |
|
||||
\*---------------------------------------------------------*/
|
||||
if(probed_name.find("G915 TKL") != std::string::npos)
|
||||
{
|
||||
LogitechG915Controller* controller = new LogitechG915Controller(dev, false, name);
|
||||
RGBController_LogitechG915* rgb_controller = new RGBController_LogitechG915(controller, true);
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
else if(probed_name.find("G915") != std::string::npos)
|
||||
{
|
||||
LogitechG915Controller* controller = new LogitechG915Controller(dev, false, name);
|
||||
RGBController_LogitechG915* rgb_controller = new RGBController_LogitechG915(controller, false);
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Unknown device (e.g. G515 or future hardware). |
|
||||
| Close and leave it for another detector to claim. |
|
||||
\*-----------------------------------------------------*/
|
||||
LOG_DEBUG("[LogitechControllerDetect] 0xC547 unrecognised device name \"%s\", skipping",
|
||||
probed_name.c_str());
|
||||
hid_close(dev);
|
||||
}
|
||||
}
|
||||
|
||||
void DetectLogitechKeyboardG915Wired(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
@@ -650,6 +758,220 @@ void DetectLogitechX56(hid_device_info* info, const std::string& name)
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------*\
|
||||
| Unified HID++ 2.0 Detection |
|
||||
| Probes IRoot (feature 0x0000) to determine if the device speaks HID++ 2.0. |
|
||||
| If it does and has RGB features, the unified controller handles it. |
|
||||
| If not, the device is released for legacy controllers. |
|
||||
\*------------------------------------------------------------------------------*/
|
||||
void DetectLogitechHIDPP20(hid_device_info* info, const std::string& /*name*/)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(!dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LogitechHIDPP20Controller* controller = new LogitechHIDPP20Controller(
|
||||
dev, info->path, LOGITECH_DEFAULT_DEVICE_INDEX, false, nullptr,
|
||||
info->usage_page);
|
||||
|
||||
if(controller->Probe())
|
||||
{
|
||||
controller->Initialize();
|
||||
|
||||
const HIDPP20DeviceCapabilities& caps = controller->GetCapabilities();
|
||||
|
||||
if(caps.has_zone_effects || caps.has_perkey)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Device has RGB features — create and register |
|
||||
| RGBController for the UI. |
|
||||
\*-------------------------------------------------*/
|
||||
RGBController_LogitechHIDPP20* rgb_controller = new RGBController_LogitechHIDPP20(controller);
|
||||
|
||||
LOG_INFO("[%s] Registering RGB controller", caps.device_name.c_str());
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Start reader + power threads immediately so we |
|
||||
| detect connection events and handle power mgmt |
|
||||
| from the start — not deferred to DeviceUpdateMode. |
|
||||
\*--------------------------------------------------*/
|
||||
if(caps.has_power_mgmt || caps.idx_wireless_status != 0)
|
||||
{
|
||||
controller->StartPowerManager();
|
||||
|
||||
if(!caps.has_power_mgmt && caps.idx_wireless_status != 0)
|
||||
{
|
||||
controller->StartEventWatcher();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(controller->HasBridge())
|
||||
{
|
||||
/*--------------------------------------------------*\
|
||||
| Centurion dongle with no sub-device — keep the |
|
||||
| controller alive and start reader thread to watch |
|
||||
| for sub-device connection events. |
|
||||
\*--------------------------------------------------*/
|
||||
LOG_INFO("[%s] Dongle registered, watching for sub-device",
|
||||
caps.device_name.c_str());
|
||||
|
||||
controller->SetRegisterCallback([](RGBController* rgb)
|
||||
{
|
||||
ResourceManager::get()->RegisterRGBController(rgb);
|
||||
});
|
||||
|
||||
controller->StartEventWatcher();
|
||||
}
|
||||
else
|
||||
{
|
||||
/*--------------------------------------------------*\
|
||||
| Device probed successfully but has no RGB and no |
|
||||
| bridge — nothing to do (e.g., headset without RGB) |
|
||||
\*--------------------------------------------------*/
|
||||
LOG_INFO("[%s] No RGB features, skipping", caps.device_name.c_str());
|
||||
delete controller;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*--------------------------------------------------*\
|
||||
| Probe failed. Could be an offline paired device, |
|
||||
| a stale pairing slot, or a receiver itself. |
|
||||
| |
|
||||
| Only skip if the name explicitly says "Receiver". |
|
||||
| Everything else gets a watcher — devices can come |
|
||||
| back at any time (power cycle, dongle swap, etc.) |
|
||||
\*--------------------------------------------------*/
|
||||
std::string hid_name;
|
||||
|
||||
if(info->product_string)
|
||||
{
|
||||
std::wstring ws(info->product_string);
|
||||
hid_name = std::string(ws.begin(), ws.end());
|
||||
}
|
||||
|
||||
if(hid_name.find("Receiver") != std::string::npos ||
|
||||
hid_name.find("receiver") != std::string::npos)
|
||||
{
|
||||
delete controller;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO("[HID++2.0 %s] Probe failed — watching for device (name='%s')",
|
||||
info->path, hid_name.c_str());
|
||||
|
||||
controller->SetRegisterCallback([](RGBController* rgb)
|
||||
{
|
||||
ResourceManager::get()->RegisterRGBController(rgb);
|
||||
});
|
||||
|
||||
controller->StartProbeWatcher();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
| Unified HID++ 2.0 Lightspeed Receiver Detection (Windows / macOS) |
|
||||
| |
|
||||
| On Linux, hid-logitech-dj splits receiver traffic into per-slot virtual child hidraw nodes with their own 0x40XX PIDs, so Linux detection can |
|
||||
| use DetectLogitechHIDPP20 directly against the virtual PIDs. Windows and macOS have no DJ driver — the receiver appears as a single HID device |
|
||||
| and we must probe each paired slot by hand, addressing it via the HID++ device_index header byte. |
|
||||
| |
|
||||
| Iterates device indices 0x01..0x06. c547 is dual-pair, but the loop covers Unifying-style receivers and any future wider-pair variants. Each |
|
||||
| responding slot gets its own hid_device handle and a shared std::mutex so sibling slots serialize HID writes — matching the pattern in the |
|
||||
| legacy LogitechLightspeedController (see CreateLogitechLightspeedDevice around line 860). |
|
||||
| |
|
||||
| TODO (untested on Windows): runtime reader-thread coordination. Each slot controller starts its own reader thread; on a shared receiver both |
|
||||
| threads will see both slots' incoming packets. The reader needs to drop frames whose device_index doesn't match its own, or dispatch across |
|
||||
| sibling controllers. Safe during probe (mutex serializes writes, reads are direct); becomes an issue post-StartPowerManager. |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
void DetectLogitechHIDPP20LightspeedReceiver(hid_device_info* info, const std::string& /*name*/)
|
||||
{
|
||||
std::shared_ptr<std::mutex> receiver_mutex = std::make_shared<std::mutex>();
|
||||
|
||||
for(uint8_t idx = 0x01; idx <= 0x06; idx++)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(!dev)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
LogitechHIDPP20Controller* controller = new LogitechHIDPP20Controller(
|
||||
dev, info->path, idx, true, receiver_mutex, info->usage_page);
|
||||
|
||||
if(!controller->Probe())
|
||||
{
|
||||
/*--------------------------------------------------*\
|
||||
| Slot is empty, stale, or not HID++ 2.0. Destructor |
|
||||
| closes the per-slot handle we opened above. |
|
||||
\*--------------------------------------------------*/
|
||||
delete controller;
|
||||
continue;
|
||||
}
|
||||
|
||||
controller->Initialize();
|
||||
|
||||
const HIDPP20DeviceCapabilities& caps = controller->GetCapabilities();
|
||||
|
||||
if(caps.has_zone_effects || caps.has_perkey)
|
||||
{
|
||||
RGBController_LogitechHIDPP20* rgb_controller = new RGBController_LogitechHIDPP20(controller);
|
||||
|
||||
LOG_INFO("[%s slot=%u] Registering RGB controller", caps.device_name.c_str(), idx);
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
|
||||
if(caps.has_power_mgmt || caps.idx_wireless_status != 0)
|
||||
{
|
||||
controller->StartPowerManager();
|
||||
|
||||
if(!caps.has_power_mgmt && caps.idx_wireless_status != 0)
|
||||
{
|
||||
controller->StartEventWatcher();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO("[%s slot=%u] No RGB features, skipping", caps.device_name.c_str(), idx);
|
||||
delete controller;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
| Unified HID++ 2.0 Devices |
|
||||
| PID-specific registrations for devices tested with the unified controller. |
|
||||
| Wired paths use the device's own USB PID; wireless paths on Linux use the hid-logitech-dj virtual child PIDs (0x40XX range). |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech HID++ 2.0 G502 X Plus (wired)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G502_X_PLUS_PID, 2, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech HID++ 2.0 G515 LS TKL (wired)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G515_LS_TKL_PID, 2, 0xFF00, 2);
|
||||
#ifdef __linux__
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech HID++ 2.0 G502 X Plus (wireless)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G502_X_PLUS_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech HID++ 2.0 G515 LS TKL (wireless)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G515_LS_TKL_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2);
|
||||
#endif
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech HID++ 2.0 Lightspeed Receiver (C547)", DetectLogitechHIDPP20LightspeedReceiver, LOGITECH_VID, 0xC547, 2, 0xFF00, 2);
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
| Centurion-transport devices (63-byte reports on usage page 0xFFA0, 0x50 addressed or 0x51 direct). |
|
||||
| Centurion receivers are not DJ-style Lightspeed receivers and are not split by hid-logitech-dj — the dongle PID enumerates as a single hidraw |
|
||||
| on all platforms. The controller's DiscoverTransport parses the report descriptor to pick the 0x50/0x51 variant and runs a 0x00..0xFF address |
|
||||
| sweep for the 0x50 (addressed) variant, so the detector only needs VID/PID + usage page 0xFFA0. |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
REGISTER_HID_DETECTOR_P("Logitech HID++ 2.0 G522 Lightspeed (wired)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G522_LIGHTSPEED_USB_PID, 0xFFA0);
|
||||
REGISTER_HID_DETECTOR_P("Logitech HID++ 2.0 G522 Lightspeed (dongle)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G522_LIGHTSPEED_DONGLE_PID, 0xFFA0);
|
||||
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
| Keyboards |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
@@ -667,7 +989,7 @@ REGISTER_HID_DETECTOR_IP ("Logitech G910 Orion Spectrum", Dete
|
||||
REGISTER_HID_DETECTOR_IP ("Logitech G Pro RGB Mechanical Gaming Keyboard", DetectLogitechKeyboardGPro, LOGITECH_VID, LOGITECH_GPRO_KEYBOARD_1_PID, 1, 0xFF43);
|
||||
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G915 Wireless RGB Mechanical Gaming Keyboard", DetectLogitechKeyboardG915, LOGITECH_VID, LOGITECH_G915_RECEIVER_PID, 2, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G915 Wireless RGB Mechanical Gaming Keyboard", DetectLogitechKeyboardG915, LOGITECH_VID, LOGITECH_G915_RECEIVER_2_PID, 2, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G915 Wireless RGB Mechanical Gaming Keyboard (Receiver 2)", DetectLogitechKeyboardG915Receiver2, LOGITECH_VID, LOGITECH_G915_RECEIVER_2_PID, 2, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G915 Wireless RGB Mechanical Gaming Keyboard (Wired)", DetectLogitechKeyboardG915Wired, LOGITECH_VID, LOGITECH_G915_WIRED_PID, 2, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G915TKL Wireless RGB Mechanical Gaming Keyboard", DetectLogitechKeyboardG915, LOGITECH_VID, LOGITECH_G915TKL_RECEIVER_PID, 2, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G915TKL Wireless RGB Mechanical Gaming Keyboard (Wired)", DetectLogitechKeyboardG915Wired, LOGITECH_VID, LOGITECH_G915TKL_WIRED_PID, 2, 0xFF00, 2);
|
||||
@@ -683,11 +1005,11 @@ REGISTER_HID_DETECTOR_IP ("Logitech G600 Gaming Mouse", Dete
|
||||
REGISTER_HID_DETECTOR_IP ("Logitech G Pro Gaming Mouse", DetectLogitechMouseGPRO, LOGITECH_VID, LOGITECH_G_PRO_PID, 1, 0xFF00);
|
||||
REGISTER_HID_DETECTOR_IP ("Logitech G Pro HERO Gaming Mouse", DetectLogitechMouseGPRO, LOGITECH_VID, LOGITECH_G_PRO_HERO_PID, 1, 0xFF00);
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
| Speakers |
|
||||
| Speakers |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G560 Lightsync Speaker", DetectLogitechG560, LOGITECH_VID, LOGITECH_G560_PID, 2, 0xFF43, 514);
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
| Headsets |
|
||||
| Headsets |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G933 Lightsync Headset", DetectLogitechG933, LOGITECH_VID, LOGITECH_G933_PID, 3, 0xFF43, 514);
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
@@ -890,12 +1212,12 @@ void DetectLogitechWireless(hid_device_info* info, const std::string& /*name*/)
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
| Lightspeed Devices (Linux Wireless) |
|
||||
| |
|
||||
| DUMMY_DEVICE_DETECTOR("Logitech G Lightspeed Receiver", DetectLogitechWireless, 0x046D, 0xC539 ) |
|
||||
/*--------------------------------------------------------------------------------------------------------------------------------------------------*\
|
||||
| Lightspeed Devices (Linux Wireless) |
|
||||
| |
|
||||
| DUMMY_DEVICE_DETECTOR("Logitech G Lightspeed Receiver", DetectLogitechWireless, 0x046D, 0xC539 ) |
|
||||
| DUMMY_DEVICE_DETECTOR("Logitech Powerplay Mat Receiver", DetectLogitechWireless, 0x046D, 0xC53A ) |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
\*--------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G403 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G403_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G502 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G502_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G703 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G703_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2);
|
||||
@@ -913,7 +1235,6 @@ REGISTER_HID_DETECTOR_IPU("Logitech Powerplay Mat",
|
||||
| G502 changed to PU to accomodate old and new firmware. Other devices may require similar update #4627 |
|
||||
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
REGISTER_HID_DETECTOR_PU("Logitech G502 Wireless Gaming Mouse (wired)", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_LIGHTSPEED_PID, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G502 X Plus (wired)", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_X_PLUS_PID, 2, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G502 Proteus Spectrum Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_PROTEUS_SPECTRUM_PID, 1, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G502 HERO Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_HERO_PID, 1, 0xFF00, 2);
|
||||
REGISTER_HID_DETECTOR_IPU("Logitech G403 Prodigy Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G403_PID, 1, 0xFF00, 2);
|
||||
|
||||
@@ -146,7 +146,7 @@ void LogitechG915Controller::SendCommit()
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev_handle, usb_buf, 4);
|
||||
hid_write(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
hid_read_timeout(dev_handle, usb_buf, RESPONSE_LEN, LOGITECH_READ_TIMEOUT);
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ void LogitechG915Controller::BeginModeSet()
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev_handle, usb_buf, 4);
|
||||
hid_write(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
hid_read(dev_handle, usb_buf, RESPONSE_LEN);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
@@ -189,7 +189,7 @@ void LogitechG915Controller::BeginModeSet()
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev_handle, usb_buf, 4);
|
||||
hid_write(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
hid_read(dev_handle, usb_buf, RESPONSE_LEN);
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ void LogitechG915Controller::InitializeModeSet()
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev_handle, usb_buf, 7);
|
||||
hid_write(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
hid_read(dev_handle, usb_buf, RESPONSE_LEN);
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ void LogitechG915Controller::InitializeDirect()
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev_handle, usb_buf, 4);
|
||||
hid_write(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
hid_read(dev_handle, usb_buf, RESPONSE_LEN);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
@@ -259,7 +259,7 @@ void LogitechG915Controller::InitializeDirect()
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev_handle, usb_buf, 4);
|
||||
hid_write(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
hid_read(dev_handle, usb_buf, RESPONSE_LEN);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
@@ -279,7 +279,7 @@ void LogitechG915Controller::InitializeDirect()
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev_handle, usb_buf, 17);
|
||||
hid_write(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
hid_read(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
@@ -300,7 +300,7 @@ void LogitechG915Controller::InitializeDirect()
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev_handle, usb_buf, 17);
|
||||
hid_write(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
hid_read(dev_handle, usb_buf, RESPONSE_LEN);
|
||||
}
|
||||
|
||||
@@ -355,7 +355,7 @@ void LogitechG915Controller::SendDirectFrame
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev_handle, usb_buf, HEADER_SIZE + length);
|
||||
hid_write(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
hid_read_timeout(dev_handle, usb_buf, RESPONSE_LEN, LOGITECH_READ_TIMEOUT);
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ void LogitechG915Controller::SendMode
|
||||
unsigned char blue
|
||||
)
|
||||
{
|
||||
unsigned char usb_buf[HEADER_SIZE + 13];
|
||||
unsigned char usb_buf[MESSAGE_LEN];
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zero out buffer |
|
||||
@@ -433,6 +433,6 @@ void LogitechG915Controller::SendMode
|
||||
/*-----------------------------------------------------*\
|
||||
| Send packet |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_write(dev_handle, usb_buf, HEADER_SIZE + 13);
|
||||
hid_write(dev_handle, usb_buf, MESSAGE_LEN);
|
||||
hid_read(dev_handle, usb_buf, RESPONSE_LEN);
|
||||
}
|
||||
|
||||
+5709
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,874 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| LogitechHIDPP20Controller.h |
|
||||
| |
|
||||
| Unified Logitech HID++ 2.0 controller |
|
||||
| |
|
||||
| Uses feature discovery (IRoot 0x0000) to dynamically |
|
||||
| determine device capabilities and adapt to any HID++ |
|
||||
| 2.0 device with RGB lighting support. |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
#include <condition_variable>
|
||||
#include <hidapi.h>
|
||||
#include "RGBController.h"
|
||||
#include "LogitechProtocolCommon.h"
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| HID++ 2.0 Feature Page IDs |
|
||||
\*-----------------------------------------------------*/
|
||||
#define HIDPP20_FEAT_IROOT 0x0000
|
||||
#define HIDPP20_FEAT_FEATURE_SET 0x0001
|
||||
#define HIDPP20_FEAT_DEVICE_NAME_TYPE 0x0005
|
||||
#define HIDPP20_FEAT_ONBOARD_PROFILES 0x8100
|
||||
#define HIDPP20_FEAT_PROFILE_MANAGEMENT 0x8101
|
||||
#define HIDPP20_FEAT_FIRMWARE_INFO 0x0003
|
||||
#define HIDPP20_FEAT_CENTPPBRIDGE 0x0003 /* same ID, different meaning on Centurion */
|
||||
#define HIDPP20_FEAT_COLOR_LED_EFFECTS 0x8070
|
||||
#define HIDPP20_FEAT_RGB_EFFECTS 0x8071
|
||||
#define HIDPP20_FEAT_PER_KEY_LIGHTING_V1 0x8080
|
||||
#define HIDPP20_FEAT_PER_KEY_LIGHTING_V2 0x8081
|
||||
#define HIDPP20_FEAT_KEYBOARD_LAYOUT 0x4540
|
||||
#define HIDPP20_FEAT_DISABLE_KEYS_BY_USAGE 0x4522
|
||||
#define HIDPP20_FEAT_CENTURION_RGB 0x0600
|
||||
#define HIDPP20_FEAT_HEADSET_RGB_HOSTMODE 0x0620
|
||||
#define HIDPP20_FEAT_CENTURION_DEVICE_INFO 0x0100
|
||||
#define HIDPP20_FEAT_CENTURION_DEVICE_NAME 0x0101
|
||||
#define HIDPP20_FEAT_UNIFIED_BATTERY 0x1004
|
||||
#define HIDPP20_FEAT_WIRELESS_STATUS 0x1D4B
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| HID++ 2.0 Function IDs (byte 3 high nibble) |
|
||||
| Function ID is shifted left 4 bits, low nibble = swID |
|
||||
\*-----------------------------------------------------*/
|
||||
/*-----------------------------------------------------*\
|
||||
| HID++ Software ID — identifies our responses. |
|
||||
| Must avoid: 0x00 (firmware), 0x01 (vendor app), |
|
||||
| 0x02-0x0F (Solaar cycles these). |
|
||||
| There are only 16 values (4-bit field), and all are |
|
||||
| claimed. We pick a fixed value and will coordinate |
|
||||
| with Solaar to exclude it from its cycle. |
|
||||
\*-----------------------------------------------------*/
|
||||
#define HIDPP20_SW_ID 0x07
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Feature 0x8071 functions |
|
||||
\*-----------------------------------------------------*/
|
||||
#define FN_8071_GET_INFO 0x00
|
||||
#define FN_8071_SET_EFFECT 0x10
|
||||
#define FN_8071_SET_PATTERN 0x20
|
||||
#define FN_8071_NV_CONFIG 0x30
|
||||
#define FN_8071_BIN_INFO 0x40
|
||||
#define FN_8071_SW_CONTROL 0x50
|
||||
#define FN_8071_SYNC 0x60
|
||||
#define FN_8071_PWR_CONFIG 0x70
|
||||
#define FN_8071_PWR_MODE 0x80
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Feature 0x8081 functions |
|
||||
\*-----------------------------------------------------*/
|
||||
#define FN_8081_GET_INFO 0x00
|
||||
#define FN_8081_SET_INDIVIDUAL 0x10
|
||||
#define FN_8081_SET_CONSECUTIVE 0x20
|
||||
#define FN_8081_SET_DELTA_5BIT 0x30
|
||||
#define FN_8081_SET_DELTA_4BIT 0x40
|
||||
#define FN_8081_SET_RANGE 0x50
|
||||
#define FN_8081_SET_SINGLE_VALUE 0x60
|
||||
#define FN_8081_FRAME_END 0x70
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Feature 0x0620 functions (headset RGB hostmode) |
|
||||
\*-----------------------------------------------------*/
|
||||
#define FN_0620_GET_INFO 0x00
|
||||
#define FN_0620_GET_RGB_ZONE_INFO 0x10
|
||||
#define FN_0620_SET_INDIVIDUAL_RGB_ZONES 0x20
|
||||
#define FN_0620_SET_CONSECUTIVE_RGB_ZONES 0x30
|
||||
#define FN_0620_SET_RANGE_RGB_ZONES 0x40
|
||||
#define FN_0620_SET_RGB_ZONES_SINGLE_VALUE 0x50
|
||||
#define FN_0620_FRAME_END 0x60
|
||||
#define FN_0620_GET_HOST_MODE_STATE 0x70
|
||||
#define FN_0620_SET_HOST_MODE_STATE 0x80
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Feature 0x8100 functions |
|
||||
\*-----------------------------------------------------*/
|
||||
#define FN_8100_SET_ONBOARD_MODE 0x10
|
||||
#define FN_8100_GET_ONBOARD_MODE 0x20
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Feature 0x8101 functions |
|
||||
\*-----------------------------------------------------*/
|
||||
#define FN_8101_GET_SET_MODE 0x60
|
||||
#define FN_8101_LOAD 0x80 // load(partition, sector, size)
|
||||
#define FN_8101_READBUFFER 0xC0 // readBuffer(offset)
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zone cluster effect entry |
|
||||
\*-----------------------------------------------------*/
|
||||
struct HIDPP20Effect
|
||||
{
|
||||
uint8_t index;
|
||||
uint16_t effect_id;
|
||||
uint16_t capabilities;
|
||||
uint16_t default_period;
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Zone cluster info from 0x8071 GetRgbClusterInfo |
|
||||
\*-----------------------------------------------------*/
|
||||
struct HIDPP20ZoneCluster
|
||||
{
|
||||
uint8_t index;
|
||||
uint16_t location;
|
||||
uint8_t effect_count;
|
||||
std::vector<HIDPP20Effect> effects;
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Per-model device quirks — behavioral differences that |
|
||||
| can't be detected via feature probing. |
|
||||
\*-----------------------------------------------------*/
|
||||
enum HIDPP20DeviceQuirks : uint32_t
|
||||
{
|
||||
HIDPP20_QUIRK_NONE = 0,
|
||||
HIDPP20_QUIRK_FADE_ACCEPTS_WRITES = (1 << 0), // firmware accepts host frames during sleep fade without waking
|
||||
};
|
||||
|
||||
struct HIDPP20DeviceQuirkEntry
|
||||
{
|
||||
uint16_t pid_wireless;
|
||||
uint16_t pid_wired;
|
||||
uint32_t quirks;
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Default: suppress frames while SLEEPING. Safe everywhere |
|
||||
| — it cannot wake a device that treats writes as activity. |
|
||||
| Devices listed here opt out of suppression because their |
|
||||
| firmware accepts writes during the fade without |
|
||||
| cancelling sleep. |
|
||||
\*---------------------------------------------------------*/
|
||||
static constexpr HIDPP20DeviceQuirkEntry HIDPP20_DEVICE_QUIRK_TABLE[] =
|
||||
{
|
||||
{ 0x40B4, 0xC355, HIDPP20_QUIRK_FADE_ACCEPTS_WRITES }, // G515 LS TKL
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Device capabilities discovered via feature probing |
|
||||
\*-----------------------------------------------------*/
|
||||
struct HIDPP20DeviceCapabilities
|
||||
{
|
||||
std::string device_name;
|
||||
uint8_t device_type;
|
||||
std::string firmware_version;
|
||||
std::string serial_number;
|
||||
std::string unit_id; // stable hardware ID (from FirmwareInfo fn0)
|
||||
uint16_t pid_wireless; // wireless virtual PID (from FirmwareInfo fn0)
|
||||
uint16_t pid_wired; // wired USB PID (from FirmwareInfo fn0)
|
||||
uint32_t quirks; // resolved from HIDPP20_DEVICE_QUIRK_TABLE after PID discovery
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Complete feature map (feature_id → runtime index) |
|
||||
| Built once by EnumerateFeatures, used by all |
|
||||
| subsequent GetFeatureIndex lookups (no wire). |
|
||||
\*--------------------------------------------------*/
|
||||
std::map<uint16_t, uint8_t> feature_map;
|
||||
std::map<uint16_t, uint8_t> feature_versions; /* feature_id -> version byte */
|
||||
bool feature_map_complete; // true after bulk enumeration
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Feature indices (0 = not supported) |
|
||||
\*-------------------------------------------------*/
|
||||
uint8_t idx_onboard_profiles;
|
||||
uint8_t idx_profile_management;
|
||||
uint8_t idx_rgb_effects;
|
||||
uint8_t idx_headset_rgb_hostmode; /* 0x0620 — Centurion headset RGB */
|
||||
uint8_t idx_perkey_v2;
|
||||
uint8_t idx_perkey_v1;
|
||||
uint8_t idx_wireless_status;
|
||||
uint8_t idx_disable_keys_by_usage; /* 0x4522 — keyboard-family handshake */
|
||||
uint16_t rgb_feature_page;
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Resolved function IDs for the RGB effects feature |
|
||||
| Varies between 0x8070, 0x8071, 0x0600 |
|
||||
\*--------------------------------------------------*/
|
||||
uint8_t fn_set_effect;
|
||||
uint8_t fn_sw_control;
|
||||
uint8_t fn_pwr_config;
|
||||
uint8_t fn_pwr_mode;
|
||||
bool has_power_mgmt;
|
||||
bool sw_control_simple;
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Persistent NV settings read from RGBEffects fn3 |
|
||||
| (FN_8071_NV_CONFIG). Capability 0x0020 is the |
|
||||
| sleep ramp / off-ramp transition (enabled + |
|
||||
| ramp_seconds). |
|
||||
\*--------------------------------------------------*/
|
||||
bool nv_sleep_ramp_known;
|
||||
bool nv_sleep_ramp_enabled;
|
||||
uint8_t nv_sleep_ramp_seconds;
|
||||
|
||||
/*---------------------------------------------------*\
|
||||
| Device-firmware effect cards (0x8071 fn0 |
|
||||
| GetEffectSpecificInfo). Populated by |
|
||||
| DiscoverEffectCards at feature-discovery time. |
|
||||
| |
|
||||
| has_effect_cards — probe returned a valid response |
|
||||
| for firmware card 0 page 1 (no InvalidArgument). |
|
||||
| effect_card_template[0..1] — device-wide constant |
|
||||
| bytes read from that response at data[10..11]. |
|
||||
| Echoed back into prep1 of DoObservedPerKeyPrep |
|
||||
| at SetEffectByIndex params[6..7]. |
|
||||
| |
|
||||
| Gate for the observed per-key prep is now |
|
||||
| has_effect_cards — replaces the earlier |
|
||||
| "effects.size() < 5" heuristic, which was a proxy |
|
||||
| that correlated with "has cards" on the devices we |
|
||||
| happened to know but had no principled meaning. |
|
||||
\*---------------------------------------------------*/
|
||||
bool has_effect_cards;
|
||||
uint8_t effect_card_template[2];
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Discovered zone and LED data |
|
||||
\*-------------------------------------------------*/
|
||||
std::vector<HIDPP20ZoneCluster> zone_clusters;
|
||||
std::vector<uint16_t> perkey_zone_ids;
|
||||
std::vector<uint8_t> headset_rgb_hostmode_zone_ids; /* 0x0620 fn1 result */
|
||||
bool has_perkey;
|
||||
bool has_zone_effects;
|
||||
bool is_headset_rgb_hostmode; /* 0x0620 path selected */
|
||||
bool has_numpad;
|
||||
uint8_t keyboard_layout_code;
|
||||
};
|
||||
|
||||
/*------------------------------------------------------*\
|
||||
| Transport type — determines wire framing |
|
||||
\*------------------------------------------------------*/
|
||||
enum HIDPP20TransportType
|
||||
{
|
||||
HIDPP20_TRANSPORT_STANDARD, // 0xFF00/0xFF43: report IDs 0x10/0x11, 7/20 bytes
|
||||
HIDPP20_TRANSPORT_CENTURION // 0xFFA0: report ID 0x51 or 0x50, 64 bytes,
|
||||
// with CPL framing and CentPPBridge sub-device routing
|
||||
};
|
||||
|
||||
/*------------------------------------------------------*\
|
||||
| Transport layer — abstracts wire format differences |
|
||||
| |
|
||||
| Standard HID++ and Centurion both carry the same |
|
||||
| feature/function/data payload, but with different |
|
||||
| report framing. This struct holds transport state |
|
||||
| so SendMessage/ReadMessage can adapt. |
|
||||
\*------------------------------------------------------*/
|
||||
struct HIDPP20Transport
|
||||
{
|
||||
HIDPP20TransportType type;
|
||||
uint16_t usage_page; // 0xFF00, 0xFF43, or 0xFFA0
|
||||
uint8_t report_id; // 0x10/0x11 for standard, 0x51/0x50 for Centurion
|
||||
bool addressed; // Centurion 0x50 has device address byte
|
||||
uint8_t device_address; // Centurion 0x50: device address (e.g., 0x23)
|
||||
uint8_t bridge_feat_idx; // CentPPBridge feature index on parent (0 if N/A)
|
||||
uint8_t sub_device_id; // CentPPBridge sub-device ID (typically 0)
|
||||
uint16_t bridge_mtu; // CentPPBridge MTU from getConnectionInfo:
|
||||
// 0 = no sub-device, sendFragment will fail
|
||||
// >0 = sub-device present, payload size in bytes
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Power management state machine |
|
||||
| Matches Solaar's RGBPowerManager states |
|
||||
\*-----------------------------------------------------*/
|
||||
enum HIDPP20PowerState
|
||||
{
|
||||
HIDPP20_POWER_ACTIVE = 0,
|
||||
HIDPP20_POWER_DIMMING = 1,
|
||||
HIDPP20_POWER_IDLE = 2,
|
||||
HIDPP20_POWER_SLEEPING = 3,
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Parsed HID++ message for the response queue |
|
||||
\*-----------------------------------------------------*/
|
||||
struct HIDPP20RawMessage
|
||||
{
|
||||
uint8_t feat;
|
||||
uint8_t func;
|
||||
uint8_t data[60];
|
||||
int result;
|
||||
};
|
||||
|
||||
/*------------------------------------------------------*\
|
||||
| Per-key write tracking. SendPerKeyData is fire-and- |
|
||||
| forget at the wire layer; we track which zones each |
|
||||
| outstanding write covers so PerKeyFrameEnd can match |
|
||||
| ACKs back by FIFO order and report which zones the |
|
||||
| firmware actually committed. |
|
||||
\*------------------------------------------------------*/
|
||||
struct OutstandingPerKeyWrite
|
||||
{
|
||||
uint8_t function; // FN_8081_* (high nibble carries the type)
|
||||
std::vector<uint8_t> zone_ids; // zones covered by this packet
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Result of a per-key frame commit. The caller uses |
|
||||
| these to update its delta-tracking state: |
|
||||
| - frame_end_acked: did the FrameEnd packet ACK? |
|
||||
| - acked_zones: zones whose write packet ACKed |
|
||||
| - attempted_zones: every zone written this frame |
|
||||
\*-----------------------------------------------------*/
|
||||
struct PerKeyFrameResult
|
||||
{
|
||||
bool frame_end_acked;
|
||||
std::vector<uint8_t> acked_zones;
|
||||
std::vector<uint8_t> attempted_zones;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------*\
|
||||
| Retry policy for SendAcked. |
|
||||
| |
|
||||
| Controls the send/read/retry loop for a single HID++ |
|
||||
| request. Three canned policies cover all use cases: |
|
||||
| |
|
||||
| Reliable: probe/discovery/set/get (~6s worst case) |
|
||||
| FrameEnd: per-key commit gate (~230ms worst) |
|
||||
| Streaming: per-key write inside the animation loop |
|
||||
| (~80ms worst) |
|
||||
| |
|
||||
| backoff_ms[i] is the delay applied BEFORE the i-th |
|
||||
| attempt. backoff_ms[0] is normally 0 (no delay before |
|
||||
| the first send). The schedule mirrors the firmware's |
|
||||
| own event burst pattern (63→125→250→500→1000→2000ms, |
|
||||
| "catch at least one of N"). |
|
||||
\*-------------------------------------------------------*/
|
||||
struct HIDPP20RetryPolicy
|
||||
{
|
||||
const uint16_t* backoff_ms; // schedule[i] = delay before attempt i
|
||||
uint8_t attempts; // length of backoff_ms (>=1)
|
||||
uint16_t read_window_ms; // per-attempt read budget
|
||||
bool flush_before; // flush response queue at call start
|
||||
bool retry_on_busy; // BUSY (0x08) -> retry the send
|
||||
const char* name; // for logging ("reliable", etc.)
|
||||
};
|
||||
|
||||
/*------------------------------------------------------*\
|
||||
| Canned backoff schedules. |
|
||||
\*------------------------------------------------------*/
|
||||
static constexpr uint16_t HIDPP20_BACKOFF_RELIABLE[] =
|
||||
{ 0, 63, 125, 250, 500, 1000, 2000 };
|
||||
static constexpr uint16_t HIDPP20_BACKOFF_PROBE[] =
|
||||
{ 0, 100 };
|
||||
|
||||
/*------------------------------------------------------*\
|
||||
| SW-control reclaim backoff. Used by ReconnectDevice |
|
||||
| to retry the claim+push sequence after a wireless |
|
||||
| reconnect, racing the firmware boot animation. The |
|
||||
| vendor app typically lands control in ~50ms; this |
|
||||
| schedule fits |
|
||||
| 6 attempts inside ~620ms so the animation never gets |
|
||||
| a chance to become visible. |
|
||||
\*------------------------------------------------------*/
|
||||
static constexpr uint16_t HIDPP20_RECLAIM_BACKOFF_MS[] =
|
||||
{ 0, 20, 40, 80, 160, 320 };
|
||||
|
||||
/*------------------------------------------------------*\
|
||||
| FrameEnd BUSY retry backoff. Used by PerKeyFrameEnd |
|
||||
| when the firmware returns HID++ error 0x08 (BUSY) |
|
||||
| because it's still draining the per-key write queue. |
|
||||
| First retry is fast (~2 USB round trips) for the |
|
||||
| common case where BUSY was transient; subsequent |
|
||||
| retries give actual drain headroom. Total worst case |
|
||||
| ~180ms, fits inside the PerKeyFrameEnd 300ms deadline |
|
||||
| with margin for the eventual ACK to land. |
|
||||
\*------------------------------------------------------*/
|
||||
static constexpr uint16_t HIDPP20_FRAME_END_BUSY_BACKOFF_MS[] =
|
||||
{ 30, 60, 90 };
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Deep-sleep detection threshold. After StartSleep() |
|
||||
| commands the firmware fade, the device eventually |
|
||||
| enters deep sleep and returns BUSY to every FrameEnd. |
|
||||
| Once this many consecutive FrameEnd attempts exhaust |
|
||||
| all BUSY retries while power_state == SLEEPING, we |
|
||||
| suppress further frame sends until Wake() fires. |
|
||||
\*-----------------------------------------------------*/
|
||||
static constexpr int HIDPP20_DEEP_SLEEP_FAILURE_THRESHOLD = 5;
|
||||
|
||||
/*------------------------------------------------------*\
|
||||
| Per-key frame retry backoff. Used when a full |
|
||||
| DeviceUpdateLEDs pass completes with some zones |
|
||||
| unacked (partial commit). The retry re-runs a whole |
|
||||
| frame from the power thread, so the backoff is |
|
||||
| between full frames, not individual packets. |
|
||||
| |
|
||||
| First value aligned to the power thread's 50ms poll |
|
||||
| cadence — anything shorter rounds up anyway, and |
|
||||
| matching the tick makes latency predictable. |
|
||||
| |
|
||||
| Worst case: 5 retries, cumulative ~1550ms. This |
|
||||
| covers the reconnect-transient window where the G502 |
|
||||
| firmware silently drops per-key writes for several |
|
||||
| hundred ms after the wireless link re-establishes. |
|
||||
\*------------------------------------------------------*/
|
||||
static constexpr uint16_t HIDPP20_REPAINT_RETRY_BACKOFF_MS[] =
|
||||
{ 50, 100, 200, 400, 800 };
|
||||
|
||||
static constexpr HIDPP20RetryPolicy HIDPP20_POLICY_RELIABLE = {
|
||||
HIDPP20_BACKOFF_RELIABLE,
|
||||
sizeof(HIDPP20_BACKOFF_RELIABLE) / sizeof(uint16_t),
|
||||
300, // read window
|
||||
true, // flush_before
|
||||
true, // retry_on_busy
|
||||
"reliable"
|
||||
};
|
||||
|
||||
/*------------------------------------------------------*\
|
||||
| Probe policy: tight budget for is-this-HID++ checks |
|
||||
| during initial discovery. ~500ms worst case for dead |
|
||||
| devices, vs ~6s for reliable. One retry handles a |
|
||||
| transient hiccup on the first IRoot call (e.g. on a |
|
||||
| busy mouse), but we bail fast on truly non-responsive |
|
||||
| or non-HID++ hidraws so probe latency stays bounded. |
|
||||
\*------------------------------------------------------*/
|
||||
static constexpr HIDPP20RetryPolicy HIDPP20_POLICY_PROBE = {
|
||||
HIDPP20_BACKOFF_PROBE,
|
||||
sizeof(HIDPP20_BACKOFF_PROBE) / sizeof(uint16_t),
|
||||
200, // read window
|
||||
true, // flush_before
|
||||
true, // retry_on_busy
|
||||
"probe"
|
||||
};
|
||||
|
||||
class LogitechHIDPP20Controller
|
||||
{
|
||||
public:
|
||||
LogitechHIDPP20Controller(hid_device* dev, const char* path,
|
||||
uint8_t device_index, bool wireless,
|
||||
std::shared_ptr<std::mutex> mutex_ptr,
|
||||
uint16_t usage_page = 0xFF00);
|
||||
~LogitechHIDPP20Controller();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Lifecycle |
|
||||
\*-------------------------------------------------*/
|
||||
bool Probe();
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Accessors |
|
||||
\*-------------------------------------------------*/
|
||||
const HIDPP20DeviceCapabilities& GetCapabilities() const;
|
||||
std::string GetDeviceLocation();
|
||||
std::string GetSerialString();
|
||||
uint32_t GetInitGeneration() const;
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Per-key lighting (0x8081) |
|
||||
| |
|
||||
| Per-key writes are fire-and-forget at the wire |
|
||||
| layer. SendPerKeyData enqueues an outstanding |
|
||||
| write entry; PerKeyFrameEnd drains the response |
|
||||
| queue, matches ACKs by FIFO, and returns which |
|
||||
| zones the firmware actually committed plus |
|
||||
| whether the FrameEnd itself ACKed. |
|
||||
\*--------------------------------------------------*/
|
||||
void SetPerKeyColors(const std::vector<std::pair<uint16_t, RGBColor>>& zone_colors);
|
||||
void SetAllPerKeyColor(RGBColor color);
|
||||
void SendPerKeyData(uint8_t perkey_idx, uint8_t function,
|
||||
const uint8_t* data, size_t len,
|
||||
const std::vector<uint8_t>& zone_ids);
|
||||
PerKeyFrameResult PerKeyFrameEnd();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Zone effects (0x8071 / 0x8070) |
|
||||
\*-------------------------------------------------*/
|
||||
void SetZoneEffect(uint8_t cluster_idx, uint8_t effect_idx,
|
||||
uint16_t effect_id,
|
||||
unsigned char r, unsigned char g, unsigned char b,
|
||||
uint16_t period, unsigned char brightness,
|
||||
unsigned char direction, bool persist);
|
||||
|
||||
/*---------------------------------------------------*\
|
||||
| Headset RGB hostmode (0x0620). |
|
||||
| Sticky-claim model: SetHostMode() claims once via |
|
||||
| fn8, then each write is fn5 (single-value) or fn2 |
|
||||
| (individual) + fn6 FrameEnd[0x01]. 0x02 persist was |
|
||||
| tested and does not work on G522 firmware. |
|
||||
\*---------------------------------------------------*/
|
||||
void SetHeadsetRGBHostmodeColors(const std::vector<RGBColor>& zone_colors);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| SW control management |
|
||||
\*-------------------------------------------------*/
|
||||
int SetSWControl(uint8_t mode, uint8_t flags);
|
||||
void SetRGBPowerMode(uint8_t mode);
|
||||
void SetHostMode();
|
||||
bool ClaimSWControlIfNeeded();
|
||||
void UpgradeSwControlAfterFirstPaint();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Keyboard-family handshake (0x4522 fn3 + fn1). |
|
||||
| G815 / G915 / G Pro send this before any mode |
|
||||
| write. Feature-gated no-op on devices (G502 / |
|
||||
| G515) that don't enumerate 0x4522. |
|
||||
\*-------------------------------------------------*/
|
||||
void DoDisableKeysByUsageHandshake();
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Keyboard-family per-key takeover prep. |
|
||||
| Per-cluster SetEffectByIndex(effectIdx=0=Off, |
|
||||
| persist=1) + primer key via SetIndividualRgbZones |
|
||||
| + FrameEnd. Matches G815 / G915 InitializeDirect. |
|
||||
| Gated on 0x4522 + per-key V2 presence. |
|
||||
\*--------------------------------------------------*/
|
||||
void DoKeyboardFamilyPerKeyPrep();
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Wake-repaint flag. Set by Wake() before calling |
|
||||
| request_repaint_fn so the repaint callback knows |
|
||||
| to invalidate sent_colors (force a full per-key |
|
||||
| push) without triggering the claim/prep sequence. |
|
||||
\*--------------------------------------------------*/
|
||||
bool ConsumeWakeFullRepaint();
|
||||
bool NeedsPrepSequence() const { return sw_control_needs_upgrade_to_5; }
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Per-key retry scheduling. Called by the RGB |
|
||||
| controller's DeviceUpdateLEDs on partial-commit |
|
||||
| frames; the power thread polls and fires |
|
||||
| request_repaint_fn when a retry deadline expires. |
|
||||
\*--------------------------------------------------*/
|
||||
void ScheduleRetryPaint();
|
||||
void CancelRetryPaint();
|
||||
void TickRetryPaintIfPending();
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Observed per-key prep sequence. Two |
|
||||
| SetEffectByIndex calls cloned byte-for-byte from a |
|
||||
| wire capture on a G502 X PLUS. |
|
||||
| Used in place of the Static-pass-through prep when |
|
||||
| the device's RGBEffects enumeration matches the |
|
||||
| G502 shape — see DeviceUpdateLEDs for gating. |
|
||||
\*--------------------------------------------------*/
|
||||
void DoObservedPerKeyPrep();
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Power management (idle/dim/sleep/wake) |
|
||||
\*--------------------------------------------------*/
|
||||
void StartPowerManager();
|
||||
void StopPowerManager();
|
||||
void StartEventWatcher();
|
||||
void StartProbeWatcher();
|
||||
bool HasBridge() const;
|
||||
void SetRepaintCallback(std::function<void()> repaint);
|
||||
void SetReapplyActiveModeCallback(std::function<bool()> cb);
|
||||
void SetRegisterCallback(std::function<void(RGBController*)> cb);
|
||||
HIDPP20PowerState GetPowerState() const;
|
||||
int GetDimBrightness() const;
|
||||
bool IsOnline() const;
|
||||
bool IsDeepSleep() const;
|
||||
void SetWireless(bool w) { wireless = w; }
|
||||
bool QueryWirelessStatus();
|
||||
bool QueryExternalPower();
|
||||
void FlushResponseQueue();
|
||||
|
||||
private:
|
||||
hid_device* dev;
|
||||
std::string location;
|
||||
uint8_t device_index;
|
||||
bool wireless;
|
||||
std::shared_ptr<std::mutex> mutex;
|
||||
HIDPP20DeviceCapabilities caps;
|
||||
HIDPP20Transport transport;
|
||||
bool initialized;
|
||||
bool sw_control_claimed;
|
||||
bool sw_control_needs_upgrade_to_5;
|
||||
uint32_t frame_counter;
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Retry-paint state (partial-commit recovery). |
|
||||
| retry_paint_deadline_ zero = no retry pending. |
|
||||
| retry_paint_attempt_ indexes into |
|
||||
| HIDPP20_REPAINT_RETRY_BACKOFF_MS; once it reaches |
|
||||
| the array length, we give up for this sequence. |
|
||||
| Atomic so both the paint thread (RGB controller) |
|
||||
| and the power thread can access without locks. |
|
||||
\*--------------------------------------------------*/
|
||||
std::atomic<std::chrono::steady_clock::time_point> retry_paint_deadline_;
|
||||
std::atomic<uint8_t> retry_paint_attempt_;
|
||||
std::atomic<bool> wake_full_repaint_pending_;
|
||||
uint32_t init_generation;
|
||||
std::string log_tag;
|
||||
|
||||
/*---------------------------------------------------*\
|
||||
| Transport-layer I/O |
|
||||
| |
|
||||
| SendMessage/ReadMessage handle wire framing based |
|
||||
| on transport.type. Upper layers pass feature index, |
|
||||
| function ID, and payload — the transport layer |
|
||||
| wraps them in the correct report format. |
|
||||
\*---------------------------------------------------*/
|
||||
int SendMessage(uint8_t feat_idx, uint8_t function,
|
||||
const uint8_t* data, size_t len);
|
||||
int ReadMessage(uint8_t* feat_idx_out, uint8_t* function_out,
|
||||
uint8_t* data_out, size_t data_max,
|
||||
int timeout_ms = LOGITECH_PROTOCOL_TIMEOUT);
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Standard HID++ transport (0xFF00/0xFF43) |
|
||||
\*--------------------------------------------------*/
|
||||
int SendStandard(uint8_t feat_idx, uint8_t function,
|
||||
const uint8_t* data, size_t len);
|
||||
int ReadStandardDirect(uint8_t* feat_idx_out, uint8_t* function_out,
|
||||
uint8_t* data_out, size_t data_max,
|
||||
int timeout_ms);
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Centurion transport (0xFFA0) |
|
||||
| Wraps messages in CPL framing, routes through |
|
||||
| CentPPBridge for sub-device access. |
|
||||
\*--------------------------------------------------*/
|
||||
int SendCenturion(uint8_t feat_idx, uint8_t function,
|
||||
const uint8_t* data, size_t len);
|
||||
int ReadCenturionDirect(uint8_t* feat_idx_out, uint8_t* function_out,
|
||||
uint8_t* data_out, size_t data_max,
|
||||
int timeout_ms);
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Reader thread dispatch layer |
|
||||
| ReadHIDDirect: raw HID read (used by reader thread |
|
||||
| and during Probe before reader starts). |
|
||||
| ReadFromQueue: waits on response queue filled by |
|
||||
| the reader thread. |
|
||||
\*--------------------------------------------------*/
|
||||
int ReadHIDDirect(uint8_t* feat_idx_out, uint8_t* function_out,
|
||||
uint8_t* data_out, size_t data_max,
|
||||
int timeout_ms);
|
||||
int ReadFromQueue(uint8_t* feat_idx_out, uint8_t* function_out,
|
||||
uint8_t* data_out, size_t data_max,
|
||||
int timeout_ms);
|
||||
|
||||
/*---------------------------------------------------*\
|
||||
| High-level helpers |
|
||||
| SendAndReceive is retained as a thin wrapper |
|
||||
| around SendAcked with the reliable policy, for |
|
||||
| call-site stability. |
|
||||
\*---------------------------------------------------*/
|
||||
int SendAndReceive(uint8_t feat_idx, uint8_t function,
|
||||
const uint8_t* send_data, size_t send_len,
|
||||
uint8_t* recv_data, size_t recv_max);
|
||||
|
||||
/*---------------------------------------------------*\
|
||||
| Unified send-and-ack primitive with retry policy. |
|
||||
| All command paths converge here. Returns: |
|
||||
| >0 : bytes copied into recv_data |
|
||||
| 0 : timeout / BUSY exhaustion |
|
||||
| -1 : non-BUSY HID++ error |
|
||||
| -2 : wire error (SendMessage failed) |
|
||||
| If hidpp20_error_out is non-null and return is -1, |
|
||||
| the HID++ error code is stored there. |
|
||||
\*---------------------------------------------------*/
|
||||
int SendAcked(uint8_t feat_idx, uint8_t function,
|
||||
const uint8_t* send_data, size_t send_len,
|
||||
uint8_t* recv_data, size_t recv_max,
|
||||
const HIDPP20RetryPolicy& policy = HIDPP20_POLICY_RELIABLE,
|
||||
uint8_t* hidpp20_error_out = nullptr);
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Compatibility shim: same as SendAcked but writes |
|
||||
| the response into a blankFAPmessage. Used by |
|
||||
| callers that inherited the SendLong+ReadResponse |
|
||||
| interface and inspect response.data[] downstream. |
|
||||
\*--------------------------------------------------*/
|
||||
int SendAckedIntoFAP(uint8_t feat_idx, uint8_t function,
|
||||
const uint8_t* send_data, size_t send_len,
|
||||
blankFAPmessage& response,
|
||||
const HIDPP20RetryPolicy& policy = HIDPP20_POLICY_RELIABLE);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Feature discovery |
|
||||
\*-------------------------------------------------*/
|
||||
uint8_t GetFeatureIndex(uint16_t feature_page,
|
||||
const HIDPP20RetryPolicy& policy = HIDPP20_POLICY_RELIABLE);
|
||||
uint8_t GetFeatureVersion(uint16_t feature_page) const;
|
||||
void DiscoverTransport();
|
||||
void DiscoverDeviceName();
|
||||
void DiscoverDeviceType();
|
||||
void EnumerateFeatures(uint8_t feature_set_idx);
|
||||
void DiscoverFirmwareInfo();
|
||||
void DiscoverRGBEffects();
|
||||
void DiscoverEffectCards();
|
||||
void DiscoverHeadsetRGBHostmode();
|
||||
void DiscoverPerKeyZones();
|
||||
void DiscoverKeyboardLayout();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Power management internals |
|
||||
\*-------------------------------------------------*/
|
||||
void ReaderThreadFunc();
|
||||
void PowerThreadFunc();
|
||||
void DispatchEvent(uint8_t feat, uint8_t func, const uint8_t* data);
|
||||
void OnUserActivity(uint8_t activity_type);
|
||||
void StartDimRamp();
|
||||
void DimRampStep();
|
||||
void StartSleep();
|
||||
void Wake();
|
||||
void ReadFirmwareTimers();
|
||||
void ReadNvSleepRampConfig();
|
||||
void WritePowerConfig(uint16_t idle_s, uint16_t sleep_s);
|
||||
void ReadActiveProfileSector();
|
||||
void ReprobeSubDevice();
|
||||
void ReconnectDevice();
|
||||
void FullReprobe();
|
||||
void RediscoverFeatures();
|
||||
/*----------------------------------------------------*\
|
||||
| Platform-specific. ScanForDevice walks the OS-level |
|
||||
| HID enumeration to find the same physical device on |
|
||||
| a new path (USB<->wireless transitions). Linux uses |
|
||||
| sysfs; Windows and macOS use hidapi + serial_number |
|
||||
| matching. Bodies live in |
|
||||
| LogitechHIDPP20Controller_Linux.cpp and |
|
||||
| LogitechHIDPP20Controller_Windows_MacOS.cpp. |
|
||||
\*----------------------------------------------------*/
|
||||
bool ScanForDevice(bool force = false);
|
||||
|
||||
/*----------------------------------------------------*\
|
||||
| Platform-specific. Returns the friendly name for a |
|
||||
| Centurion sub-device at the given hidapi path, or "" |
|
||||
| if no name is available. Linux reads HID_NAME from |
|
||||
| sysfs; Windows uses hid_device_info::product_string. |
|
||||
\*----------------------------------------------------*/
|
||||
std::string GetCenturionSubDeviceName(const std::string& path);
|
||||
|
||||
void SwapHIDHandle(hid_device* new_dev, const std::string& new_path);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Reader thread + response queue |
|
||||
\*-------------------------------------------------*/
|
||||
std::thread* reader_thread;
|
||||
std::atomic<bool> reader_running;
|
||||
std::mutex response_mutex;
|
||||
std::condition_variable response_cv;
|
||||
std::deque<HIDPP20RawMessage> response_queue;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Power thread (state machine + command sender) |
|
||||
\*-------------------------------------------------*/
|
||||
std::thread* power_thread;
|
||||
std::atomic<bool> power_thread_running;
|
||||
std::atomic<int> pending_activity; // -1=none, 0=idle, 1+=active
|
||||
std::atomic<int> pending_connection; // 0=none, +1=connected, -1=disconnected
|
||||
std::atomic<int> pending_path_check; // HID++1.0 DJ notification → force-scan retries remaining (0=idle)
|
||||
std::atomic<bool> device_online; // false when device is unreachable
|
||||
std::atomic<int> consecutive_timeouts; // reset on successful response
|
||||
std::atomic<bool> watcher_mode; // true when retrying failed probe
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Power management state |
|
||||
\*-------------------------------------------------*/
|
||||
HIDPP20PowerState power_state;
|
||||
std::mutex power_mutex;
|
||||
std::atomic<bool> deep_sleep; // true once device stops responding after StartSleep()
|
||||
std::atomic<int> consecutive_frame_end_failures; // FrameEnd BUSY exhaustions while SLEEPING
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Dim ramp state |
|
||||
| dim_brightness_pct is applied by DeviceUpdateLEDs |
|
||||
| to scale colors before pushing to device. |
|
||||
| This is our own host-side animation, independent |
|
||||
| of any firmware dim/sleep timers. |
|
||||
\*-------------------------------------------------*/
|
||||
#define DIM_STEPS 25
|
||||
#define DIM_INTERVAL_MS 200
|
||||
#define DIM_TARGET_PCT 50
|
||||
std::atomic<int> dim_brightness_pct; // 100=full, 50=dimmed
|
||||
int dim_step;
|
||||
std::chrono::steady_clock::time_point next_dim_time;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Sleep timer |
|
||||
\*-------------------------------------------------*/
|
||||
std::chrono::steady_clock::time_point sleep_deadline;
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Last idle-settings re-read timestamp. Drives the |
|
||||
| 500ms poll in PowerThreadFunc that re-reads the |
|
||||
| LogitechHIDPP20IdleSettings JSON key so updates |
|
||||
| from the plugin (or manual edits) apply within |
|
||||
| about half a second without any callback plumbing. |
|
||||
\*--------------------------------------------------*/
|
||||
std::chrono::steady_clock::time_point last_idle_poll;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Effective idle/sleep timers used by the state |
|
||||
| machine. Populated from the firmware snapshot by |
|
||||
| default, then possibly overridden by profile |
|
||||
| values in ApplyPowerSavingProfile. |
|
||||
\*-------------------------------------------------*/
|
||||
uint16_t idle_timeout_s;
|
||||
uint16_t sleep_timeout_s;
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Firmware-configured timer snapshot. Read at init |
|
||||
| (and on reconnect) by ReadFirmwareTimers and |
|
||||
| never overwritten by profile application, so |
|
||||
| that the unconfigured fallback path and transition |
|
||||
| back from a user profile both have a clean set |
|
||||
| of defaults to return to. |
|
||||
\*--------------------------------------------------*/
|
||||
uint16_t fw_idle_timeout_s = 60;
|
||||
uint16_t fw_sleep_timeout_s = 300;
|
||||
uint16_t written_idle_s = 0; // last value written to device RAM (0 = not written yet)
|
||||
uint16_t written_sleep_s = 0;
|
||||
|
||||
/*--------------------------------------------------*\
|
||||
| Host-side idle/dim/sleep state. |
|
||||
| Populated from LogitechHIDPP20IdleSettings on each |
|
||||
| ApplyPowerSavingProfile() invocation. |
|
||||
\*--------------------------------------------------*/
|
||||
bool ps_dim_enabled = false;
|
||||
int ps_dim_target_pct = DIM_TARGET_PCT;
|
||||
bool ps_sleep_enabled = false;
|
||||
bool ps_on_external_power = false;
|
||||
bool ps_last_logged_external = false;
|
||||
int ps_last_logged_pct = -1;
|
||||
int ps_last_logged_idle = -1;
|
||||
int ps_last_logged_sleep = -1;
|
||||
uint16_t last_power_raw = 0xFFFF; // dedup for QueryExternalPower TRACE
|
||||
uint8_t idx_unified_battery = 0;
|
||||
|
||||
void ApplyPowerSavingProfile();
|
||||
bool IsCurrentlyWireless() const;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Per-key write tracking (per active frame) |
|
||||
| Populated by SendPerKeyData, drained by |
|
||||
| PerKeyFrameEnd. Single-threaded — only the RGB |
|
||||
| controller thread touches per-key state. |
|
||||
\*-------------------------------------------------*/
|
||||
std::vector<OutstandingPerKeyWrite> outstanding_writes;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Callbacks |
|
||||
\*-------------------------------------------------*/
|
||||
std::function<void()> request_repaint_fn;
|
||||
std::function<bool()> reapply_active_mode_fn;
|
||||
std::function<void(RGBController*)> register_controller_fn;
|
||||
};
|
||||
+433
@@ -0,0 +1,433 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| LogitechHIDPP20Controller_Linux.cpp |
|
||||
| |
|
||||
| Linux-specific path-migration and device-name lookup |
|
||||
| for the unified Logitech HID++ 2.0 controller. |
|
||||
| |
|
||||
| Uses sysfs (/sys/class/hidraw) to find the same |
|
||||
| physical device on a new hidraw path after a USB <-> |
|
||||
| wireless transition, and to read Centurion sub-device |
|
||||
| friendly names from HID_NAME uevent fields. |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <dirent.h>
|
||||
#include "LogitechHIDPP20Controller.h"
|
||||
#include "LogManager.h"
|
||||
|
||||
#define LOG_TAG log_tag.c_str()
|
||||
|
||||
std::string LogitechHIDPP20Controller::GetCenturionSubDeviceName(const std::string& path)
|
||||
{
|
||||
/*--------------------------------------------------------*\
|
||||
| Centurion sub-device friendly name comes from sysfs |
|
||||
| HID_NAME=, same field Solaar reads. The `path` arg is a |
|
||||
| hidraw dev path like /dev/hidraw5; extract the hidrawN |
|
||||
| basename and read /sys/class/hidraw/<basename>/device/ |
|
||||
| uevent. |
|
||||
\*--------------------------------------------------------*/
|
||||
std::string sysfs_name;
|
||||
size_t pos = path.rfind("hidraw");
|
||||
|
||||
if(pos == std::string::npos)
|
||||
{
|
||||
return sysfs_name;
|
||||
}
|
||||
|
||||
std::string uevent_path = "/sys/class/hidraw/" + path.substr(pos) + "/device/uevent";
|
||||
FILE* f = fopen(uevent_path.c_str(), "r");
|
||||
|
||||
if(!f)
|
||||
{
|
||||
return sysfs_name;
|
||||
}
|
||||
|
||||
char line[256];
|
||||
|
||||
while(fgets(line, sizeof(line), f))
|
||||
{
|
||||
if(strncmp(line, "HID_NAME=", 9) == 0)
|
||||
{
|
||||
sysfs_name = line + 9;
|
||||
|
||||
while(!sysfs_name.empty() && (sysfs_name.back() == '\n' || sysfs_name.back() == '\r'))
|
||||
{
|
||||
sysfs_name.pop_back();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return sysfs_name;
|
||||
}
|
||||
|
||||
bool LogitechHIDPP20Controller::ScanForDevice(bool force)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Scan sysfs for a hidraw with matching unitId. Called by |
|
||||
| the power thread either periodically (normal reactive |
|
||||
| mode — only when device_online==false, USB/wireless |
|
||||
| transition or physical unplug) or on demand from the |
|
||||
| reader thread after a HID++1.0 Device Connection |
|
||||
| notification arrives (force=true, bypasses the online |
|
||||
| gate). Finds the device on its new connection path. |
|
||||
| |
|
||||
| Reactive-only: we never migrate while the current path |
|
||||
| still works. On devices like the G502 X PLUS that expose |
|
||||
| both a USB-direct hidraw AND a wireless-via-dongle hidraw |
|
||||
| simultaneously (when paired to the receiver and plugged |
|
||||
| in at the same time), eager migration would bounce us off |
|
||||
| a working path onto one the firmware has actively muted, |
|
||||
| breaking control. The device signals which path is |
|
||||
| active by returning errors/going silent on the inactive |
|
||||
| path — our reader thread picks that up as a hid_read |
|
||||
| failure and flips device_online=false, at which point the |
|
||||
| power thread calls this function to pick the new path. |
|
||||
| |
|
||||
| Linux implementation: walks /sys/class/hidraw and matches |
|
||||
| on HID_UNIQ. The Windows counterpart (same class method, |
|
||||
| different .cpp file) uses hid_enumerate + serial_number. |
|
||||
| |
|
||||
| Match criteria: |
|
||||
| - device_online == false (caller should already ensure) |
|
||||
| - HID_UNIQ matches our unitId |
|
||||
| - Logitech VID (046D) |
|
||||
| - HID++ interface (usage_page 0xFF00, usage 2) |
|
||||
| - Different from our current path (device moved) |
|
||||
\*---------------------------------------------------------*/
|
||||
if(caps.unit_id.empty() || caps.unit_id == "00000000")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Online guard: never migrate off a working path unless |
|
||||
| the caller explicitly forces a re-check. The normal |
|
||||
| periodic scan path stays gated on device_online==false |
|
||||
| so it's a no-op when everything is healthy. |
|
||||
| |
|
||||
| The reader thread bypasses this guard (force=true) after |
|
||||
| seeing a HID++1.0 Device Connection Status notification |
|
||||
| from the Lightspeed receiver, which fires BEFORE the |
|
||||
| firmware fully switches its data flow from wireless to |
|
||||
| USB. At that moment device_online is still true (the |
|
||||
| current path hasn't failed yet), but we want the scan to |
|
||||
| run so we can migrate to the new path proactively. |
|
||||
\*---------------------------------------------------------*/
|
||||
if(!force && device_online.load())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Normalize our unitId to lowercase hex without dashes for |
|
||||
| comparison. Sysfs HID_UNIQ varies between drivers: |
|
||||
| - Lightspeed virtual: "0d-12-5d-47" (dashes) |
|
||||
| - USB direct: "0D125D47" (no dashes) |
|
||||
| We strip dashes and lowercase both sides for matching. |
|
||||
\*---------------------------------------------------------*/
|
||||
std::string target_norm;
|
||||
|
||||
for(char c : caps.unit_id)
|
||||
{
|
||||
if(c != '-')
|
||||
{
|
||||
target_norm += (char)tolower(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Read our current PID from sysfs. Only migrate to paths |
|
||||
| with a DIFFERENT PID — same PID means same receiver, |
|
||||
| just a different pairing slot (e.g., stale pairing). |
|
||||
\*---------------------------------------------------------*/
|
||||
unsigned int current_pid = 0;
|
||||
{
|
||||
std::string cur_hidraw = location.substr(location.rfind('/') + 1);
|
||||
std::string cur_uevent = "/sys/class/hidraw/" + cur_hidraw + "/device/uevent";
|
||||
std::ifstream cur_file(cur_uevent);
|
||||
std::string line;
|
||||
|
||||
while(std::getline(cur_file, line))
|
||||
{
|
||||
if(line.compare(0, 7, "HID_ID=") == 0)
|
||||
{
|
||||
size_t lc = line.rfind(':');
|
||||
|
||||
if(lc != std::string::npos)
|
||||
{
|
||||
sscanf(line.c_str() + lc + 1, "%x", ¤t_pid);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------*\
|
||||
| Collect ALL sysfs hidraws with matching unit_id + Logitech |
|
||||
| VID + different PID. With multiple Lightspeed dongles in |
|
||||
| the system, several hidraws can share the same HID_UNIQ: |
|
||||
| one is the live virt-slot on the connected dongle, others |
|
||||
| are stale virt-slots on dongles where our device is not |
|
||||
| actually present. We have to probe each one to find out |
|
||||
| which slot is real — sysfs alone can't tell them apart. |
|
||||
\*----------------------------------------------------------*/
|
||||
struct Candidate
|
||||
{
|
||||
std::string dev_path;
|
||||
unsigned int pid;
|
||||
};
|
||||
|
||||
std::vector<Candidate> candidates;
|
||||
|
||||
DIR* dir = opendir("/sys/class/hidraw");
|
||||
|
||||
if(!dir)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
|
||||
while((entry = readdir(dir)) != nullptr)
|
||||
{
|
||||
if(strncmp(entry->d_name, "hidraw", 6) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string uevent_path = "/sys/class/hidraw/" +
|
||||
std::string(entry->d_name) + "/device/uevent";
|
||||
std::ifstream uevent(uevent_path);
|
||||
|
||||
if(!uevent.is_open())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
std::string hid_uniq;
|
||||
std::string hid_id;
|
||||
|
||||
while(std::getline(uevent, line))
|
||||
{
|
||||
if(line.compare(0, 9, "HID_UNIQ=") == 0)
|
||||
{
|
||||
hid_uniq = line.substr(9);
|
||||
}
|
||||
else if(line.compare(0, 7, "HID_ID=") == 0)
|
||||
{
|
||||
hid_id = line.substr(7);
|
||||
}
|
||||
}
|
||||
|
||||
std::string uniq_norm;
|
||||
|
||||
for(char c : hid_uniq)
|
||||
{
|
||||
if(c != '-')
|
||||
{
|
||||
uniq_norm += (char)tolower(c);
|
||||
}
|
||||
}
|
||||
|
||||
if(uniq_norm != target_norm)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(hid_id.find("0000046D") == std::string::npos &&
|
||||
hid_id.find("0000046d") == std::string::npos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string dev_path = "/dev/" + std::string(entry->d_name);
|
||||
|
||||
if(dev_path == location)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t last_colon = hid_id.rfind(':');
|
||||
|
||||
if(last_colon == std::string::npos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int pid = 0;
|
||||
sscanf(hid_id.c_str() + last_colon + 1, "%x", &pid);
|
||||
|
||||
if(pid == current_pid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
candidates.push_back({dev_path, pid});
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
if(candidates.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------*\
|
||||
| Probe each candidate before committing. Two-dongle systems |
|
||||
| can expose multiple sysfs hidraws with the same HID_UNIQ: |
|
||||
| one is the live slot, others are stale pairings that |
|
||||
| respond with short-format UNKNOWN_DEVICE errors. Issue a |
|
||||
| cheap IRoot GetFeature (feat 0x0001) to distinguish them. |
|
||||
| A live slot returns a long-form response with feat_idx=0 |
|
||||
| and feat_byte matching the sub-index we asked for. A stale |
|
||||
| slot returns r[10 xx 8F 00 00 08 ...] (short error, code |
|
||||
| 0x08 UNKNOWN_DEVICE). We accept the first candidate that |
|
||||
| passes; the overall pending_path_check retry loop will |
|
||||
| naturally re-probe all candidates on subsequent scans if |
|
||||
| none pass on the current pass (e.g., mid-transition). |
|
||||
\*----------------------------------------------------------*/
|
||||
std::string found_path;
|
||||
hid_device* found_dev = nullptr;
|
||||
|
||||
for(size_t c = 0; c < candidates.size(); c++)
|
||||
{
|
||||
const Candidate& cand = candidates[c];
|
||||
|
||||
LOG_DEBUG("%s Scan: migration candidate at %s (pid=0x%04X, current=%s pid=0x%04X)",
|
||||
LOG_TAG, cand.dev_path.c_str(), cand.pid,
|
||||
location.c_str(), current_pid);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Multi-dongle caveat: hid_enumerate returns entries |
|
||||
| for ALL dongles with this PID (046D:4099 Lightspeed |
|
||||
| can appear several times in a single machine). Match |
|
||||
| strictly on the sysfs candidate's dev_path so each |
|
||||
| candidate resolves to its OWN dongle's HID++ |
|
||||
| interface — not the first match we stumble across. |
|
||||
\*-----------------------------------------------------*/
|
||||
hid_device_info* devs = hid_enumerate(0x046D, (uint16_t)cand.pid);
|
||||
std::string hidpp20_path;
|
||||
|
||||
for(hid_device_info* d = devs; d != nullptr; d = d->next)
|
||||
{
|
||||
LOG_TRACE("%s Scan: enumerate PID=0x%04X path=%s page=0x%04X usage=%d",
|
||||
LOG_TAG, cand.pid, d->path, d->usage_page, d->usage);
|
||||
|
||||
if(std::string(d->path) == cand.dev_path &&
|
||||
d->usage_page == 0xFF00 && d->usage == 2 &&
|
||||
std::string(d->path) != location)
|
||||
{
|
||||
hidpp20_path = d->path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hid_free_enumeration(devs);
|
||||
|
||||
if(hidpp20_path.empty())
|
||||
{
|
||||
LOG_DEBUG("%s Scan: %s no matching LogitechHID++ interface in enum",
|
||||
LOG_TAG, cand.dev_path.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
hid_device* test_dev = hid_open_path(hidpp20_path.c_str());
|
||||
|
||||
if(!test_dev)
|
||||
{
|
||||
LOG_DEBUG("%s Scan: %s failed to open", LOG_TAG, hidpp20_path.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------*\
|
||||
| Probe: HID++2.0 IRoot GetFeature for feat 0x0001 |
|
||||
| (IFeatureSet). Wire: w[10 FF 0000 000100] |
|
||||
| - report_id 0x10 (short) |
|
||||
| - device_index 0xFF |
|
||||
| - feat_idx 0x00 (IRoot) |
|
||||
| - address 0x00 (func 0 GetFeature, sw_id 0) |
|
||||
| - payload 00 01 00 (feat_id 0x0001) |
|
||||
| |
|
||||
| A live device returns r[11 xx 00 0X ...] — long form, |
|
||||
| feat_idx 0x00, the address byte we sent back, and the |
|
||||
| feature index in the payload. |
|
||||
| A stale slot returns r[10 xx 8F 00 00 08 ...] — short |
|
||||
| error form. Reject anything that starts with 0x10. |
|
||||
\*------------------------------------------------------*/
|
||||
uint8_t probe[7] = {0x10, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00};
|
||||
uint8_t reply[20] = {};
|
||||
int write_rc = hid_write(test_dev, probe, sizeof(probe));
|
||||
bool probe_ok = false;
|
||||
|
||||
if(write_rc >= 0)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Drain up to ~100ms, looking for a long-form response |
|
||||
| matching our probe. Skip stray reports from unrelated |
|
||||
| firmware events that might be queued on the hidraw. |
|
||||
\*-----------------------------------------------------*/
|
||||
std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() +
|
||||
std::chrono::milliseconds(100);
|
||||
|
||||
while(std::chrono::steady_clock::now() < deadline)
|
||||
{
|
||||
int read_rc = hid_read_timeout(test_dev, reply, sizeof(reply), 50);
|
||||
|
||||
if(read_rc <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(reply[0] == 0x11 && reply[2] == 0x00 && reply[3] == 0x00)
|
||||
{
|
||||
probe_ok = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(reply[0] == 0x10 && reply[2] == 0x8F)
|
||||
{
|
||||
LOG_DEBUG("%s Scan: %s probe rejected — err=0x%02X (stale slot)",
|
||||
LOG_TAG, hidpp20_path.c_str(), reply[5]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!probe_ok)
|
||||
{
|
||||
hid_close(test_dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_DEBUG("%s Scan: %s probe accepted (feat_idx=0x%02X)",
|
||||
LOG_TAG, hidpp20_path.c_str(), reply[4]);
|
||||
|
||||
found_path = hidpp20_path;
|
||||
found_dev = test_dev;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!found_dev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("%s Device migrated: %s -> %s", LOG_TAG, location.c_str(), found_path.c_str());
|
||||
|
||||
SwapHIDHandle(found_dev, found_path);
|
||||
return true;
|
||||
}
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| LogitechHIDPP20Controller_Windows_MacOS.cpp |
|
||||
| |
|
||||
| Path-migration and device-name lookup for the unified |
|
||||
| Logitech HID++ 2.0 controller on Windows and macOS. |
|
||||
| |
|
||||
| Uses hidapi's hid_enumerate + hid_device_info fields |
|
||||
| (serial_number, product_string, product_id, usage_page) |
|
||||
| on platforms with no /sys/class/hidraw equivalent. |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "LogitechHIDPP20Controller.h"
|
||||
#include "LogManager.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#define LOG_TAG log_tag.c_str()
|
||||
|
||||
std::string LogitechHIDPP20Controller::GetCenturionSubDeviceName(const std::string& path)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| On Windows, hidapi's hid_device_info carries a |
|
||||
| product_string field (wchar_t*). Enumerate all Logitech |
|
||||
| devices and find the one whose path matches `path`. |
|
||||
| |
|
||||
| Caveat: Windows hidapi typically returns the parent |
|
||||
| product string on every (interface, usage_page, usage) |
|
||||
| entry that maps to the same USB device, so Centurion |
|
||||
| sub-devices may share a name with the parent dongle. |
|
||||
| That's a less specific name than Linux's HID_NAME, but |
|
||||
| still better than "Logitech Centurion Device". |
|
||||
\*---------------------------------------------------------*/
|
||||
std::string friendly;
|
||||
|
||||
hid_device_info* devs = hid_enumerate(0x046D, 0x0000);
|
||||
|
||||
for(hid_device_info* d = devs; d != nullptr; d = d->next)
|
||||
{
|
||||
if(d->path == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(std::string(d->path) != path)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
friendly = StringUtils::wchar_to_string(d->product_string);
|
||||
break;
|
||||
}
|
||||
|
||||
hid_free_enumeration(devs);
|
||||
return friendly;
|
||||
}
|
||||
|
||||
bool LogitechHIDPP20Controller::ScanForDevice(bool force)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Scan hidapi for a Logitech device with matching unitId. |
|
||||
| Called by the power thread either periodically (normal |
|
||||
| reactive mode — only when device_online==false, USB/ |
|
||||
| wireless transition or physical unplug) or on demand |
|
||||
| from the reader thread after a HID++1.0 Device |
|
||||
| Connection notification arrives (force=true, bypasses |
|
||||
| the online gate). Finds the device on its new |
|
||||
| connection path. |
|
||||
| |
|
||||
| Reactive-only: we never migrate while the current path |
|
||||
| still works (same rationale as Linux — see the Linux |
|
||||
| companion file for the full explanation). |
|
||||
| |
|
||||
| Windows implementation: walks hid_enumerate(046D, *) and |
|
||||
| matches on hid_device_info::serial_number, which for |
|
||||
| Logitech devices generally corresponds to the same |
|
||||
| stable identity as the HID++-reported unit_id. If |
|
||||
| serial_number matching yields nothing (hidapi may not |
|
||||
| populate it for some Logitech devices on Windows), we |
|
||||
| fall through to IRoot probing every candidate on the |
|
||||
| matching usage_page/usage so we can still find the |
|
||||
| device albeit more slowly. |
|
||||
\*---------------------------------------------------------*/
|
||||
if(caps.unit_id.empty() || caps.unit_id == "00000000")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!force && device_online.load())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string target_norm = StringUtils::normalize_hex_id(caps.unit_id);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Candidate = {path, pid, has_serial_match}. Serial matches |
|
||||
| sort first so we try the cheapest/most-likely candidate. |
|
||||
\*---------------------------------------------------------*/
|
||||
struct Candidate
|
||||
{
|
||||
std::string dev_path;
|
||||
unsigned int pid;
|
||||
bool serial_match;
|
||||
};
|
||||
|
||||
std::vector<Candidate> candidates;
|
||||
unsigned int current_pid = 0;
|
||||
|
||||
hid_device_info* devs = hid_enumerate(0x046D, 0x0000);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| First pass: find the entry matching our current path and |
|
||||
| record its product_id so we can skip same-PID candidates. |
|
||||
\*---------------------------------------------------------*/
|
||||
for(hid_device_info* d = devs; d != nullptr; d = d->next)
|
||||
{
|
||||
if(d->path != nullptr && std::string(d->path) == location)
|
||||
{
|
||||
current_pid = d->product_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Second pass: collect candidates that (a) aren't our |
|
||||
| current path, (b) speak the HID++ interface, and (c) have |
|
||||
| a plausible identity match — either the serial_number |
|
||||
| matches our unit_id, or at minimum their PID differs from |
|
||||
| ours so they can't be another slot on the same dongle. |
|
||||
\*---------------------------------------------------------*/
|
||||
for(hid_device_info* d = devs; d != nullptr; d = d->next)
|
||||
{
|
||||
if(d->path == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(std::string(d->path) == location)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Only HID++ interface (usage_page 0xFF00, usage 2). |
|
||||
| Note: on Windows hidapi may or may not populate |
|
||||
| usage / usage_page consistently. If either is zero, |
|
||||
| fall through — we'll probe unconditionally in that |
|
||||
| case rather than dropping a potential candidate. |
|
||||
\*-----------------------------------------------------*/
|
||||
bool usage_known = (d->usage_page != 0 || d->usage != 0);
|
||||
|
||||
if(usage_known && (d->usage_page != 0xFF00 || d->usage != 2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Compare serial_number (wchar_t*) against our unit_id. |
|
||||
| Normalize both and do an exact-match comparison. |
|
||||
| Empty/missing serial is allowed — falls through as a |
|
||||
| serial_match=false candidate so the probe path can |
|
||||
| still find it via a different-PID filter. |
|
||||
\*-----------------------------------------------------*/
|
||||
bool serial_match = false;
|
||||
|
||||
if(d->serial_number != nullptr && d->serial_number[0] != L'\0')
|
||||
{
|
||||
std::string sn = StringUtils::wchar_to_string(d->serial_number);
|
||||
std::string sn_norm = StringUtils::normalize_hex_id(sn);
|
||||
|
||||
if(!sn_norm.empty() && sn_norm == target_norm)
|
||||
{
|
||||
serial_match = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| If we have no serial match AND this path shares the |
|
||||
| current PID, skip. Same PID with no identity evidence |
|
||||
| is probably a sibling slot on the same dongle, not a |
|
||||
| valid migration target. |
|
||||
\*-----------------------------------------------------*/
|
||||
if(!serial_match && d->product_id == current_pid && current_pid != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Candidate c;
|
||||
c.dev_path = d->path;
|
||||
c.pid = d->product_id;
|
||||
c.serial_match = serial_match;
|
||||
candidates.push_back(c);
|
||||
}
|
||||
|
||||
hid_free_enumeration(devs);
|
||||
|
||||
if(candidates.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Sort so serial-matched candidates get probed first. |
|
||||
\*---------------------------------------------------------*/
|
||||
std::stable_sort(candidates.begin(), candidates.end(),
|
||||
[](const Candidate& a, const Candidate& b)
|
||||
{
|
||||
return a.serial_match && !b.serial_match;
|
||||
});
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Probe each candidate with IRoot GetFeature feat 0x0001. |
|
||||
| A live slot returns a long-form (0x11) response; a stale |
|
||||
| slot returns a short-form (0x10) error. See the Linux |
|
||||
| companion file for the wire-level explanation. |
|
||||
\*---------------------------------------------------------*/
|
||||
std::string found_path;
|
||||
hid_device* found_dev = nullptr;
|
||||
|
||||
for(size_t c = 0; c < candidates.size(); c++)
|
||||
{
|
||||
const Candidate& cand = candidates[c];
|
||||
|
||||
LOG_DEBUG("%s Scan: migration candidate at %s (pid=0x%04X, serial_match=%d)",
|
||||
LOG_TAG, cand.dev_path.c_str(), cand.pid,
|
||||
cand.serial_match ? 1 : 0);
|
||||
|
||||
hid_device* test_dev = hid_open_path(cand.dev_path.c_str());
|
||||
|
||||
if(!test_dev)
|
||||
{
|
||||
LOG_DEBUG("%s Scan: %s failed to open",
|
||||
LOG_TAG, cand.dev_path.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t probe[7] = {0x10, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00};
|
||||
uint8_t reply[20] = {};
|
||||
int write_rc = hid_write(test_dev, probe, sizeof(probe));
|
||||
bool probe_ok = false;
|
||||
|
||||
if(write_rc >= 0)
|
||||
{
|
||||
std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() +
|
||||
std::chrono::milliseconds(100);
|
||||
|
||||
while(std::chrono::steady_clock::now() < deadline)
|
||||
{
|
||||
int read_rc = hid_read_timeout(test_dev, reply, sizeof(reply), 50);
|
||||
|
||||
if(read_rc <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(reply[0] == 0x11 && reply[2] == 0x00 && reply[3] == 0x00)
|
||||
{
|
||||
probe_ok = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(reply[0] == 0x10 && reply[2] == 0x8F)
|
||||
{
|
||||
LOG_DEBUG("%s Scan: %s probe rejected — err=0x%02X (stale slot)",
|
||||
LOG_TAG, cand.dev_path.c_str(), reply[5]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!probe_ok)
|
||||
{
|
||||
hid_close(test_dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_DEBUG("%s Scan: %s probe accepted (feat_idx=0x%02X)",
|
||||
LOG_TAG, cand.dev_path.c_str(), reply[4]);
|
||||
|
||||
found_path = cand.dev_path;
|
||||
found_dev = test_dev;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!found_dev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("%s Device migrated: %s -> %s",
|
||||
LOG_TAG, location.c_str(), found_path.c_str());
|
||||
|
||||
SwapHIDHandle(found_dev, found_path);
|
||||
return true;
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| LogitechHIDPP20IdleSettings.cpp |
|
||||
| |
|
||||
| Host-side idle/dim/sleep settings storage helper for |
|
||||
| Logitech HID++ 2.0 devices. Loads the configuration |
|
||||
| block from SettingsManager JSON, caches it, and writes |
|
||||
| changes back through SettingsManager::SetSettings(). |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "LogitechHIDPP20IdleSettings.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "SettingsManager.h"
|
||||
|
||||
static const char* SETTINGS_KEY = "LogitechHIDPP20IdleSettings";
|
||||
|
||||
LogitechHIDPP20IdleSettings* LogitechHIDPP20IdleSettings::instance()
|
||||
{
|
||||
static LogitechHIDPP20IdleSettings inst;
|
||||
return &inst;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Minimum idle / sleep values the controller will honor. |
|
||||
| Matches what Logitech firmware typically clamps to on |
|
||||
| HID++ 2.0 devices, and guarantees the skip-dim path's |
|
||||
| sleep_delay math always produces a positive window. |
|
||||
\*---------------------------------------------------------*/
|
||||
static const int MIN_IDLE_S = 15;
|
||||
static const int MIN_SLEEP_S = 45;
|
||||
static const int MIN_DIM_WINDOW = 30; // sleep must exceed idle by at least this
|
||||
|
||||
static void ClampProfile(LogitechHIDPP20IdleProfile& p)
|
||||
{
|
||||
if(p.dim_brightness < 0) p.dim_brightness = 0;
|
||||
if(p.dim_brightness > 100) p.dim_brightness = 100;
|
||||
|
||||
if(p.idle_timeout_s < MIN_IDLE_S)
|
||||
{
|
||||
p.idle_timeout_s = MIN_IDLE_S;
|
||||
}
|
||||
|
||||
if(p.sleep_timeout_s < MIN_SLEEP_S)
|
||||
{
|
||||
p.sleep_timeout_s = MIN_SLEEP_S;
|
||||
}
|
||||
|
||||
if(p.sleep_timeout_s < p.idle_timeout_s + MIN_DIM_WINDOW)
|
||||
{
|
||||
p.sleep_timeout_s = p.idle_timeout_s + MIN_DIM_WINDOW;
|
||||
}
|
||||
}
|
||||
|
||||
static LogitechHIDPP20IdleProfile ProfileFromJson(const json& j)
|
||||
{
|
||||
LogitechHIDPP20IdleProfile p;
|
||||
|
||||
if(j.contains("dim_when_idle")) p.dim_when_idle = j["dim_when_idle"];
|
||||
if(j.contains("dim_brightness")) p.dim_brightness = j["dim_brightness"];
|
||||
if(j.contains("idle_timeout_s")) p.idle_timeout_s = j["idle_timeout_s"];
|
||||
if(j.contains("allow_sleep")) p.allow_sleep = j["allow_sleep"];
|
||||
if(j.contains("sleep_timeout_s")) p.sleep_timeout_s = j["sleep_timeout_s"];
|
||||
|
||||
ClampProfile(p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static json ProfileToJson(const LogitechHIDPP20IdleProfile& p)
|
||||
{
|
||||
json j;
|
||||
|
||||
j["dim_when_idle"] = p.dim_when_idle;
|
||||
j["dim_brightness"] = p.dim_brightness;
|
||||
j["idle_timeout_s"] = p.idle_timeout_s;
|
||||
j["allow_sleep"] = p.allow_sleep;
|
||||
j["sleep_timeout_s"] = p.sleep_timeout_s;
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
void LogitechHIDPP20IdleSettings::load()
|
||||
{
|
||||
json settings = ResourceManager::get()->GetSettingsManager()->GetSettings(SETTINGS_KEY);
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Empty / missing key means the plugin is not in use. |
|
||||
| Reset both profiles to defaults with configured=false so |
|
||||
| the controller defers to firmware. |
|
||||
\*---------------------------------------------------------*/
|
||||
if(!settings.is_object() || settings.empty())
|
||||
{
|
||||
configured = false;
|
||||
on_battery = LogitechHIDPP20IdleProfile{};
|
||||
plugged_in = LogitechHIDPP20IdleProfile{};
|
||||
return;
|
||||
}
|
||||
|
||||
configured = true;
|
||||
|
||||
if(settings.contains("on_battery"))
|
||||
{
|
||||
on_battery = ProfileFromJson(settings["on_battery"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
on_battery = LogitechHIDPP20IdleProfile{};
|
||||
}
|
||||
|
||||
if(settings.contains("plugged_in"))
|
||||
{
|
||||
plugged_in = ProfileFromJson(settings["plugged_in"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
plugged_in = LogitechHIDPP20IdleProfile{};
|
||||
}
|
||||
}
|
||||
|
||||
void LogitechHIDPP20IdleSettings::save()
|
||||
{
|
||||
json settings;
|
||||
|
||||
settings["on_battery"] = ProfileToJson(on_battery);
|
||||
settings["plugged_in"] = ProfileToJson(plugged_in);
|
||||
|
||||
SettingsManager* mgr = ResourceManager::get()->GetSettingsManager();
|
||||
mgr->SetSettings(SETTINGS_KEY, settings);
|
||||
mgr->SaveSettings();
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
void LogitechHIDPP20IdleSettings::setOnBattery(const LogitechHIDPP20IdleProfile& p)
|
||||
{
|
||||
on_battery = p;
|
||||
configured = true;
|
||||
}
|
||||
|
||||
void LogitechHIDPP20IdleSettings::setPluggedIn(const LogitechHIDPP20IdleProfile& p)
|
||||
{
|
||||
plugged_in = p;
|
||||
configured = true;
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| LogitechHIDPP20IdleSettings.h |
|
||||
| |
|
||||
| Host-side idle/dim/sleep configuration for Logitech |
|
||||
| HID++ 2.0 devices. Two profiles (on_battery, plugged_in)|
|
||||
| selected at runtime based on the device's external- |
|
||||
| power flag. `configured == false` means the JSON key |
|
||||
| is absent entirely — the controller defers to firmware. |
|
||||
| Qt-free so the controller can consume it directly. |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct LogitechHIDPP20IdleProfile
|
||||
{
|
||||
bool dim_when_idle = false;
|
||||
int dim_brightness = 50;
|
||||
int idle_timeout_s = 60;
|
||||
bool allow_sleep = false;
|
||||
int sleep_timeout_s = 300;
|
||||
};
|
||||
|
||||
class LogitechHIDPP20IdleSettings
|
||||
{
|
||||
public:
|
||||
static LogitechHIDPP20IdleSettings* instance();
|
||||
|
||||
void load();
|
||||
void save();
|
||||
|
||||
bool isConfigured() const { return configured; }
|
||||
const LogitechHIDPP20IdleProfile& onBattery() const { return on_battery; }
|
||||
const LogitechHIDPP20IdleProfile& pluggedIn() const { return plugged_in; }
|
||||
|
||||
void setOnBattery(const LogitechHIDPP20IdleProfile& p);
|
||||
void setPluggedIn(const LogitechHIDPP20IdleProfile& p);
|
||||
|
||||
private:
|
||||
LogitechHIDPP20IdleSettings() = default;
|
||||
|
||||
bool configured = false;
|
||||
LogitechHIDPP20IdleProfile on_battery;
|
||||
LogitechHIDPP20IdleProfile plugged_in;
|
||||
};
|
||||
+1796
File diff suppressed because it is too large
Load Diff
+72
@@ -0,0 +1,72 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_LogitechHIDPP20.h |
|
||||
| |
|
||||
| RGBController for unified Logitech HID++ 2.0 devices |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RGBController.h"
|
||||
#include "LogitechHIDPP20Controller.h"
|
||||
|
||||
class RGBController_LogitechHIDPP20 : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_LogitechHIDPP20(LogitechHIDPP20Controller* controller_ptr);
|
||||
~RGBController_LogitechHIDPP20();
|
||||
|
||||
void SetupZones();
|
||||
void ResizeZone(int zone, int new_size);
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
void DeviceUpdateMode();
|
||||
void DeviceSaveMode();
|
||||
bool ReapplyActiveMode();
|
||||
|
||||
private:
|
||||
LogitechHIDPP20Controller* controller;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Repaint callback handler. Registered with the controller |
|
||||
| as request_repaint_fn and invoked from the power thread |
|
||||
| for dim/wake when no animation is driving updates. |
|
||||
\*---------------------------------------------------------*/
|
||||
void OnRepaintRequest();
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| When true, the next DeviceUpdateMode cycle sends its |
|
||||
| SetZoneEffect calls with persist=true instead of the |
|
||||
| default ephemeral write. Used by DeviceSaveMode to replay |
|
||||
| the active mode as a NVM-committed effect on 0x8070 |
|
||||
| devices, which default to non-persistent live writes. |
|
||||
\*---------------------------------------------------------*/
|
||||
bool save_pending = false;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Maps OpenRGB LED index -> HID++ per-key zone ID |
|
||||
\*---------------------------------------------------------*/
|
||||
std::vector<uint16_t> led_to_zone_id;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Reverse map: zone_id -> LED index (-1 if no LED). |
|
||||
| Indexed 0..255 (zone IDs are bytes). Built once in |
|
||||
| SetupZones to avoid scanning led_to_zone_id at commit |
|
||||
| time, which would be O(N) per acked zone. |
|
||||
\*---------------------------------------------------------*/
|
||||
std::vector<int> zone_id_to_led_idx;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Last successfully committed colors for delta updates. |
|
||||
| An entry of HIDPP20_UNCOMMITTED (0xFF000000) marks an LED |
|
||||
| whose last write didn't ACK and which therefore needs to |
|
||||
| be re-pushed in the next frame regardless of color delta. |
|
||||
| The high byte (0xFF) is impossible for any value produced |
|
||||
| by ToRGBColor() so it never collides with a real color. |
|
||||
\*---------------------------------------------------------*/
|
||||
std::vector<RGBColor> sent_colors;
|
||||
uint32_t last_init_gen = 0;
|
||||
};
|
||||
@@ -68,11 +68,13 @@ REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Gaming X Trio",
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Gaming X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, MSI_SUB_VEN, MSI_RTX4090_GAMING_X_SLIM_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Suprim Liquid X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, MSI_SUB_VEN, MSI_RTX4090_SUPRIM_LIQUID_X_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Suprim X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, MSI_SUB_VEN, MSI_RTX4090_SUPRIM_X_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5070 Gaming Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, MSI_SUB_VEN, MSI_RTX5070_GAMING_TRIO_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5070 Ti Gaming Trio OC Plus", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, MSI_SUB_VEN, MSI_RTX5070TI_GAMING_TRIO_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5070 Ti VANGUARD SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, MSI_SUB_VEN, MSI_RTX5070TI_VANGUARD_SOC_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 Gaming Trio OC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_GAMING_TRIO_OC_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 VANGUARD OC SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_VANGUARD_SOC_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 SUPRIM SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_SUPRIM_SOC_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 SUPRIM LIQUID SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_SUPRIM_LIQUID_SOC_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 Gaming Trio OC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_GAMING_TRIO_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 VANGUARD SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_VANGUARD_SOC_SUB_DEV, 0x68);
|
||||
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 SUPRIM SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_SUPRIM_SOC_SUB_DEV, 0x68);
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| MSILaptopController.cpp |
|
||||
| |
|
||||
| Driver for MSI laptop SteelSeries KLC/ALC RGB devices |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <cstring>
|
||||
#include "MSILaptopController.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#define MSI_LAPTOP_REPORT_ID 0x00
|
||||
#define MSI_LAPTOP_COMMAND 0x0C
|
||||
#define MSI_LAPTOP_KLC_PACKET_ID 0x66
|
||||
#define MSI_LAPTOP_ALC_PACKET_ID 0x06
|
||||
#define MSI_LAPTOP_PACKET_SIZE 525
|
||||
#define MSI_LAPTOP_PAYLOAD_OFFSET 5
|
||||
|
||||
MSILaptopController::MSILaptopController(hid_device* dev_handle, const char* path, std::string dev_name, msi_laptop_device device_type)
|
||||
{
|
||||
dev = dev_handle;
|
||||
location = path;
|
||||
name = dev_name;
|
||||
type = device_type;
|
||||
}
|
||||
|
||||
MSILaptopController::~MSILaptopController()
|
||||
{
|
||||
hid_close(dev);
|
||||
}
|
||||
|
||||
std::string MSILaptopController::GetDeviceLocation()
|
||||
{
|
||||
return("HID: " + location);
|
||||
}
|
||||
|
||||
std::string MSILaptopController::GetDeviceName()
|
||||
{
|
||||
return(name);
|
||||
}
|
||||
|
||||
std::string MSILaptopController::GetSerialString()
|
||||
{
|
||||
wchar_t serial_string[128];
|
||||
int ret = hid_get_serial_number_string(dev, serial_string, 128);
|
||||
|
||||
if(ret != 0)
|
||||
{
|
||||
return("");
|
||||
}
|
||||
|
||||
return(StringUtils::wstring_to_string(serial_string));
|
||||
}
|
||||
|
||||
msi_laptop_device MSILaptopController::GetDeviceType()
|
||||
{
|
||||
return(type);
|
||||
}
|
||||
|
||||
void MSILaptopController::SetLEDs(std::vector<led> leds, std::vector<RGBColor> colors)
|
||||
{
|
||||
unsigned char buf[MSI_LAPTOP_PACKET_SIZE];
|
||||
unsigned int led_count = (leds.size() < colors.size()) ? leds.size() : colors.size();
|
||||
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
|
||||
buf[0x00] = MSI_LAPTOP_REPORT_ID;
|
||||
buf[0x01] = MSI_LAPTOP_COMMAND;
|
||||
buf[0x03] = (type == MSI_LAPTOP_KLC) ? MSI_LAPTOP_KLC_PACKET_ID : MSI_LAPTOP_ALC_PACKET_ID;
|
||||
|
||||
// Fill unused LED IDs with 0xFF so they are ignored by the controller
|
||||
for(int i = 0; i < (MSI_LAPTOP_PACKET_SIZE - MSI_LAPTOP_PAYLOAD_OFFSET) / 4; i++)
|
||||
{
|
||||
buf[MSI_LAPTOP_PAYLOAD_OFFSET + (i * 4)] = 0xFF;
|
||||
}
|
||||
|
||||
for(unsigned int led_idx = 0; led_idx < led_count; led_idx++)
|
||||
{
|
||||
unsigned int offset = MSI_LAPTOP_PAYLOAD_OFFSET + (led_idx * 4);
|
||||
|
||||
if((offset + 3) >= sizeof(buf))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
buf[offset + 0] = leds[led_idx].value;
|
||||
buf[offset + 1] = RGBGetRValue(colors[led_idx]);
|
||||
buf[offset + 2] = RGBGetGValue(colors[led_idx]);
|
||||
buf[offset + 3] = RGBGetBValue(colors[led_idx]);
|
||||
}
|
||||
|
||||
hid_send_feature_report(dev, buf, sizeof(buf));
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| MSILaptopController.h |
|
||||
| |
|
||||
| Driver for MSI laptop SteelSeries KLC/ALC RGB devices |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <hidapi.h>
|
||||
#include "RGBController.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MSI_LAPTOP_KLC,
|
||||
MSI_LAPTOP_ALC,
|
||||
|
||||
} msi_laptop_device;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
unsigned char id;
|
||||
|
||||
} msi_laptop_led;
|
||||
|
||||
struct MSILaptopModel
|
||||
{
|
||||
const char* sys_vendor;
|
||||
const char* product_name;
|
||||
|
||||
/* Keyboard layout */
|
||||
const msi_laptop_led* klc_leds;
|
||||
unsigned int klc_leds_count;
|
||||
unsigned int klc_matrix_height;
|
||||
unsigned int klc_matrix_width;
|
||||
const unsigned int* klc_matrix_map;
|
||||
|
||||
/* Lightbar layout */
|
||||
const msi_laptop_led* alc_leds;
|
||||
unsigned int alc_leds_count;
|
||||
unsigned int alc_lightbar_leds;
|
||||
};
|
||||
|
||||
class MSILaptopController
|
||||
{
|
||||
public:
|
||||
MSILaptopController(hid_device* dev_handle, const char* path, std::string dev_name, msi_laptop_device device_type);
|
||||
~MSILaptopController();
|
||||
|
||||
std::string GetDeviceLocation();
|
||||
std::string GetDeviceName();
|
||||
std::string GetSerialString();
|
||||
msi_laptop_device GetDeviceType();
|
||||
|
||||
void SetLEDs(std::vector<led> leds, std::vector<RGBColor> colors);
|
||||
|
||||
private:
|
||||
hid_device* dev;
|
||||
std::string location;
|
||||
std::string name;
|
||||
msi_laptop_device type;
|
||||
};
|
||||
@@ -0,0 +1,235 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| MSILaptopControllerDetect.cpp |
|
||||
| |
|
||||
| Detector for MSI laptop SteelSeries RGB devices |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "Detector.h"
|
||||
#include "RGBControllerKeyNames.h"
|
||||
#include "RGBController_MSILaptop.h"
|
||||
#include "MSILaptopController.h"
|
||||
#include "dmiinfo.h"
|
||||
|
||||
#define MSI_LAPTOP_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#define STEELSERIES_VID 0x1038
|
||||
#define STEELSERIES_MSI_RAIDER_A18_KLC_PID 0x1122
|
||||
#define STEELSERIES_MSI_RAIDER_A18_ALC_PID 0x1161
|
||||
|
||||
#define NA 0xFFFFFFFF
|
||||
#define MSI_LAPTOP_KLC_MATRIX_HEIGHT 6
|
||||
#define MSI_LAPTOP_KLC_MATRIX_WIDTH 23
|
||||
#define MSI_LAPTOP_ALC_LIGHTBAR_LEDS 3
|
||||
|
||||
static const msi_laptop_led msi_raider_a18_klc_leds[] =
|
||||
{
|
||||
{ KEY_EN_A, 0x04 },
|
||||
{ KEY_EN_B, 0x05 },
|
||||
{ KEY_EN_C, 0x06 },
|
||||
{ KEY_EN_D, 0x07 },
|
||||
{ KEY_EN_E, 0x08 },
|
||||
{ KEY_EN_F, 0x09 },
|
||||
{ KEY_EN_G, 0x0A },
|
||||
{ KEY_EN_H, 0x0B },
|
||||
{ KEY_EN_I, 0x0C },
|
||||
{ KEY_EN_J, 0x0D },
|
||||
{ KEY_EN_K, 0x0E },
|
||||
{ KEY_EN_L, 0x0F },
|
||||
{ KEY_EN_M, 0x10 },
|
||||
{ KEY_EN_N, 0x11 },
|
||||
{ KEY_EN_O, 0x12 },
|
||||
{ KEY_EN_P, 0x13 },
|
||||
{ KEY_EN_Q, 0x14 },
|
||||
{ KEY_EN_R, 0x15 },
|
||||
{ KEY_EN_S, 0x16 },
|
||||
{ KEY_EN_T, 0x17 },
|
||||
{ KEY_EN_U, 0x18 },
|
||||
{ KEY_EN_V, 0x19 },
|
||||
{ KEY_EN_W, 0x1A },
|
||||
{ KEY_EN_X, 0x1B },
|
||||
{ KEY_EN_Y, 0x1C },
|
||||
{ KEY_EN_Z, 0x1D },
|
||||
{ KEY_EN_1, 0x1E },
|
||||
{ KEY_EN_2, 0x1F },
|
||||
{ KEY_EN_3, 0x20 },
|
||||
{ KEY_EN_4, 0x21 },
|
||||
{ KEY_EN_5, 0x22 },
|
||||
{ KEY_EN_6, 0x23 },
|
||||
{ KEY_EN_7, 0x24 },
|
||||
{ KEY_EN_8, 0x25 },
|
||||
{ KEY_EN_9, 0x26 },
|
||||
{ KEY_EN_0, 0x27 },
|
||||
{ KEY_EN_ESCAPE, 0x29 },
|
||||
{ KEY_EN_TAB, 0x2B },
|
||||
{ KEY_EN_SPACE, 0x2C },
|
||||
{ KEY_EN_MINUS, 0x2D },
|
||||
{ KEY_EN_EQUALS, 0x2E },
|
||||
{ KEY_EN_LEFT_BRACKET, 0x2F },
|
||||
{ KEY_EN_RIGHT_BRACKET, 0x30 },
|
||||
{ KEY_EN_SEMICOLON, 0x33 },
|
||||
{ KEY_EN_QUOTE, 0x34 },
|
||||
{ KEY_EN_BACK_TICK, 0x35 },
|
||||
{ KEY_EN_COMMA, 0x36 },
|
||||
{ KEY_EN_PERIOD, 0x37 },
|
||||
{ KEY_EN_FORWARD_SLASH, 0x38 },
|
||||
{ KEY_EN_CAPS_LOCK, 0x39 },
|
||||
{ KEY_EN_F1, 0x3A },
|
||||
{ KEY_EN_F2, 0x3B },
|
||||
{ KEY_EN_F3, 0x3C },
|
||||
{ KEY_EN_F4, 0x3D },
|
||||
{ KEY_EN_F5, 0x3E },
|
||||
{ KEY_EN_F6, 0x3F },
|
||||
{ KEY_EN_F7, 0x40 },
|
||||
{ KEY_EN_F8, 0x41 },
|
||||
{ KEY_EN_F9, 0x42 },
|
||||
{ KEY_EN_F10, 0x43 },
|
||||
{ KEY_EN_F11, 0x44 },
|
||||
{ KEY_EN_F12, 0x45 },
|
||||
{ KEY_EN_PRINT_SCREEN, 0x46 },
|
||||
{ KEY_EN_SCROLL_LOCK, 0x47 },
|
||||
{ KEY_EN_INSERT, 0x49 },
|
||||
{ "Home/Page Up", 0x4B },
|
||||
{ KEY_EN_DELETE, 0x4C },
|
||||
{ KEY_EN_PAGE_DOWN, 0x4E },
|
||||
{ KEY_EN_RIGHT_ARROW, 0x4F },
|
||||
{ KEY_EN_LEFT_ARROW, 0x50 },
|
||||
{ KEY_EN_DOWN_ARROW, 0x51 },
|
||||
{ KEY_EN_UP_ARROW, 0x52 },
|
||||
{ KEY_EN_NUMPAD_LOCK, 0x53 },
|
||||
{ KEY_EN_NUMPAD_DIVIDE, 0x54 },
|
||||
{ KEY_EN_NUMPAD_TIMES, 0x55 },
|
||||
{ KEY_EN_NUMPAD_MINUS, 0x56 },
|
||||
{ KEY_EN_NUMPAD_PLUS, 0x57 },
|
||||
{ KEY_EN_NUMPAD_ENTER, 0x58 },
|
||||
{ KEY_EN_NUMPAD_1, 0x59 },
|
||||
{ KEY_EN_NUMPAD_2, 0x5A },
|
||||
{ KEY_EN_NUMPAD_3, 0x5B },
|
||||
{ KEY_EN_NUMPAD_4, 0x5C },
|
||||
{ KEY_EN_NUMPAD_5, 0x5D },
|
||||
{ KEY_EN_NUMPAD_6, 0x5E },
|
||||
{ KEY_EN_NUMPAD_7, 0x5F },
|
||||
{ KEY_EN_NUMPAD_8, 0x60 },
|
||||
{ KEY_EN_NUMPAD_9, 0x61 },
|
||||
{ KEY_EN_NUMPAD_0, 0x62 },
|
||||
{ KEY_EN_NUMPAD_PERIOD, 0x63 },
|
||||
{ KEY_EN_POWER, 0x66 },
|
||||
{ KEY_EN_LEFT_CONTROL, 0xE0 },
|
||||
{ KEY_EN_LEFT_SHIFT, 0xE1 },
|
||||
{ KEY_EN_LEFT_ALT, 0xE2 },
|
||||
{ KEY_EN_LEFT_WINDOWS, 0xE3 },
|
||||
{ KEY_EN_RIGHT_WINDOWS, 0xE4 },
|
||||
{ KEY_EN_RIGHT_FUNCTION, 0xF0 },
|
||||
{ KEY_EN_ANSI_ENTER, 0x28 },
|
||||
{ KEY_EN_BACKSPACE, 0x2A },
|
||||
{ KEY_EN_BACK_SLASH, 0x31 },
|
||||
{ KEY_EN_ISO_BACK_SLASH, 0x64 },
|
||||
{ KEY_EN_RIGHT_SHIFT, 0xE5 },
|
||||
{ KEY_EN_RIGHT_ALT, 0xE6 },
|
||||
};
|
||||
|
||||
static unsigned int msi_raider_a18_klc_matrix_map[MSI_LAPTOP_KLC_MATRIX_HEIGHT][MSI_LAPTOP_KLC_MATRIX_WIDTH] =
|
||||
{
|
||||
{ 36, NA, 50, 51, 52, 53, NA, 54, 55, 56, 57, NA, 58, 59, 60, 61, 62, 63, NA, NA, NA, NA, 89 },
|
||||
{ 45, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 39, 40, 97, NA, 64, 65, NA, 72, 73, 74, 75, NA },
|
||||
{ 37, 16, 22, 4, 17, 19, 24, 20, 8, 14, 15, 41, 42, 98, NA, 66, 67, NA, 84, 85, 86, 76, NA },
|
||||
{ 49, 0, 18, 3, 5, 6, 7, 9, 10, 11, 43, 44, 96, NA, NA, NA, NA, NA, 81, 82, 83, NA, NA },
|
||||
{ 91, 25, 23, 2, 21, 1, 13, 12, 46, 47, 48,100, NA, NA, NA, NA, 71, NA, 78, 79, 80, 77, NA },
|
||||
{ 90, 93, 92, NA, NA, NA, 38, NA, NA, NA,101, 94, 95, NA, 69, 70, 68, NA, 87, NA, 88, NA, NA },
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Note on Raider A18 HX ALC (Lightbar) LED mappings: |
|
||||
| Initial reverse engineering packet captures showed 6 zones|
|
||||
| being updated (indexes 0x00 to 0x05). |
|
||||
| However, indexes 0x04 (L4) and 0x05 (L5) are "dummy" zones|
|
||||
| on this specific model and do not correspond to physical |
|
||||
| LEDs. To avoid user confusion, they are omitted from the |
|
||||
| UI profile here. |
|
||||
| The SetLEDs function handles zero-padding the unused |
|
||||
| payload slots with 0xFF so that omitting these zones |
|
||||
| doesn't accidentally overwrite the 0x00 index (L1). |
|
||||
\*---------------------------------------------------------*/
|
||||
static const msi_laptop_led msi_raider_a18_alc_leds[] =
|
||||
{
|
||||
{ "Lightbar 1", 0x00 },
|
||||
{ "Lightbar 2", 0x01 },
|
||||
{ "Lightbar 3", 0x02 },
|
||||
{ "Logo", 0x03 },
|
||||
};
|
||||
|
||||
static const MSILaptopModel msi_laptop_models[] =
|
||||
{
|
||||
{
|
||||
"Micro-Star International Co., Ltd.",
|
||||
"Raider A18 HX A9WJG",
|
||||
|
||||
/* Keyboard layout */
|
||||
msi_raider_a18_klc_leds,
|
||||
MSI_LAPTOP_ARRAY_SIZE(msi_raider_a18_klc_leds),
|
||||
MSI_LAPTOP_KLC_MATRIX_HEIGHT,
|
||||
MSI_LAPTOP_KLC_MATRIX_WIDTH,
|
||||
(const unsigned int*)msi_raider_a18_klc_matrix_map,
|
||||
|
||||
/* Lightbar layout */
|
||||
msi_raider_a18_alc_leds,
|
||||
MSI_LAPTOP_ARRAY_SIZE(msi_raider_a18_alc_leds),
|
||||
MSI_LAPTOP_ALC_LIGHTBAR_LEDS,
|
||||
},
|
||||
};
|
||||
|
||||
static const MSILaptopModel* GetMSILaptopModelDMI()
|
||||
{
|
||||
DMIInfo dmi;
|
||||
|
||||
for(unsigned int i = 0; i < MSI_LAPTOP_ARRAY_SIZE(msi_laptop_models); i++)
|
||||
{
|
||||
if((dmi.getManufacturer() == msi_laptop_models[i].sys_vendor) &&
|
||||
(dmi.getProductName() == msi_laptop_models[i].product_name))
|
||||
{
|
||||
return &msi_laptop_models[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DetectMSILaptop(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
const MSILaptopModel* model = GetMSILaptopModelDMI();
|
||||
if(!model)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
msi_laptop_device device_type;
|
||||
|
||||
if(info->product_id == STEELSERIES_MSI_RAIDER_A18_KLC_PID)
|
||||
{
|
||||
device_type = MSI_LAPTOP_KLC;
|
||||
}
|
||||
else if(info->product_id == STEELSERIES_MSI_RAIDER_A18_ALC_PID)
|
||||
{
|
||||
device_type = MSI_LAPTOP_ALC;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(dev)
|
||||
{
|
||||
MSILaptopController* controller = new MSILaptopController(dev, info->path, name, device_type);
|
||||
RGBController_MSILaptop* rgb_controller = new RGBController_MSILaptop(controller, model);
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_HID_DETECTOR("MSI Laptop Keyboard", DetectMSILaptop, STEELSERIES_VID, STEELSERIES_MSI_RAIDER_A18_KLC_PID);
|
||||
REGISTER_HID_DETECTOR_I("MSI Laptop Lightbar", DetectMSILaptop, STEELSERIES_VID, STEELSERIES_MSI_RAIDER_A18_ALC_PID, 0);
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_MSILaptop.cpp |
|
||||
| |
|
||||
| RGBController for MSI laptop SteelSeries RGB devices |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "RGBControllerKeyNames.h"
|
||||
#include "RGBController_MSILaptop.h"
|
||||
|
||||
#define NA 0xFFFFFFFF
|
||||
/**------------------------------------------------------------------*\
|
||||
@name MSI Laptop SteelSeries RGB
|
||||
@category Keyboard
|
||||
@type USB
|
||||
@save :x:
|
||||
@direct :white_check_mark:
|
||||
@effects :x:
|
||||
@detectors DetectMSILaptop
|
||||
@comment
|
||||
\*-------------------------------------------------------------------*/
|
||||
|
||||
RGBController_MSILaptop::RGBController_MSILaptop(MSILaptopController* controller_ptr, const MSILaptopModel* model_ptr)
|
||||
{
|
||||
controller = controller_ptr;
|
||||
model = model_ptr;
|
||||
|
||||
name = controller->GetDeviceName();
|
||||
vendor = "SteelSeries";
|
||||
description = std::string(model->sys_vendor) + " " + std::string(model->product_name) + " RGB Device";
|
||||
location = controller->GetDeviceLocation();
|
||||
serial = controller->GetSerialString();
|
||||
type = (controller->GetDeviceType() == MSI_LAPTOP_KLC) ? DEVICE_TYPE_KEYBOARD : DEVICE_TYPE_LEDSTRIP;
|
||||
|
||||
mode Direct;
|
||||
Direct.name = "Direct";
|
||||
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
|
||||
Direct.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Direct);
|
||||
|
||||
SetupZones();
|
||||
}
|
||||
|
||||
RGBController_MSILaptop::~RGBController_MSILaptop()
|
||||
{
|
||||
for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++)
|
||||
{
|
||||
if(zones[zone_idx].matrix_map != NULL)
|
||||
{
|
||||
delete zones[zone_idx].matrix_map;
|
||||
}
|
||||
}
|
||||
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void RGBController_MSILaptop::SetupZones()
|
||||
{
|
||||
if(controller->GetDeviceType() == MSI_LAPTOP_KLC)
|
||||
{
|
||||
zone keyboard_zone;
|
||||
|
||||
keyboard_zone.name = ZONE_EN_KEYBOARD;
|
||||
keyboard_zone.type = ZONE_TYPE_MATRIX;
|
||||
keyboard_zone.leds_min = model->klc_leds_count;
|
||||
keyboard_zone.leds_max = model->klc_leds_count;
|
||||
keyboard_zone.leds_count = model->klc_leds_count;
|
||||
keyboard_zone.matrix_map = new matrix_map_type;
|
||||
keyboard_zone.matrix_map->height = model->klc_matrix_height;
|
||||
keyboard_zone.matrix_map->width = model->klc_matrix_width;
|
||||
keyboard_zone.matrix_map->map = (unsigned int *)model->klc_matrix_map;
|
||||
|
||||
zones.push_back(keyboard_zone);
|
||||
|
||||
for(unsigned int led_idx = 0; led_idx < model->klc_leds_count; led_idx++)
|
||||
{
|
||||
led new_led;
|
||||
new_led.name = model->klc_leds[led_idx].name;
|
||||
new_led.value = model->klc_leds[led_idx].id;
|
||||
leds.push_back(new_led);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
zone lightbar_zone;
|
||||
lightbar_zone.name = "Lightbar";
|
||||
lightbar_zone.type = ZONE_TYPE_LINEAR;
|
||||
lightbar_zone.start_idx = 0;
|
||||
lightbar_zone.leds_min = model->alc_lightbar_leds;
|
||||
lightbar_zone.leds_max = model->alc_lightbar_leds;
|
||||
lightbar_zone.leds_count = model->alc_lightbar_leds;
|
||||
lightbar_zone.matrix_map = NULL;
|
||||
zones.push_back(lightbar_zone);
|
||||
|
||||
zone logo_zone;
|
||||
logo_zone.name = "Logo";
|
||||
logo_zone.type = ZONE_TYPE_SINGLE;
|
||||
logo_zone.start_idx = model->alc_lightbar_leds;
|
||||
logo_zone.leds_min = 1;
|
||||
logo_zone.leds_max = 1;
|
||||
logo_zone.leds_count = 1;
|
||||
logo_zone.matrix_map = NULL;
|
||||
zones.push_back(logo_zone);
|
||||
|
||||
for(unsigned int led_idx = 0; led_idx < model->alc_leds_count; led_idx++)
|
||||
{
|
||||
led new_led;
|
||||
new_led.name = model->alc_leds[led_idx].name;
|
||||
new_led.value = model->alc_leds[led_idx].id;
|
||||
leds.push_back(new_led);
|
||||
}
|
||||
}
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
void RGBController_MSILaptop::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||
{
|
||||
}
|
||||
|
||||
void RGBController_MSILaptop::DeviceUpdateLEDs()
|
||||
{
|
||||
controller->SetLEDs(leds, colors);
|
||||
}
|
||||
|
||||
void RGBController_MSILaptop::UpdateZoneLEDs(int /*zone*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_MSILaptop::UpdateSingleLED(int /*led*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_MSILaptop::DeviceUpdateMode()
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_MSILaptop.h |
|
||||
| |
|
||||
| RGBController for MSI laptop SteelSeries RGB devices |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RGBController.h"
|
||||
#include "MSILaptopController.h"
|
||||
|
||||
class RGBController_MSILaptop : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_MSILaptop(MSILaptopController* controller_ptr, const MSILaptopModel* model_ptr);
|
||||
~RGBController_MSILaptop();
|
||||
|
||||
void SetupZones();
|
||||
|
||||
void ResizeZone(int zone, int new_size);
|
||||
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
|
||||
void DeviceUpdateMode();
|
||||
|
||||
private:
|
||||
MSILaptopController* controller;
|
||||
const MSILaptopModel* model;
|
||||
};
|
||||
@@ -0,0 +1,121 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_MSIMonitor.cpp |
|
||||
| |
|
||||
| RGBController for MSI monitor (gaming controller) |
|
||||
| |
|
||||
| Andy Herbert 2026 June 4 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <string.h>
|
||||
#include "MSIMonitorController.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
MSIMonitorController::MSIMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name)
|
||||
{
|
||||
dev = dev_handle;
|
||||
location = info.path;
|
||||
name = dev_name;
|
||||
}
|
||||
|
||||
MSIMonitorController::~MSIMonitorController()
|
||||
{
|
||||
hid_close(dev);
|
||||
}
|
||||
|
||||
std::string MSIMonitorController::GetDeviceLocation()
|
||||
{
|
||||
return("HID: " + location);
|
||||
}
|
||||
|
||||
std::string MSIMonitorController::GetNameString()
|
||||
{
|
||||
return(name);
|
||||
}
|
||||
|
||||
std::string MSIMonitorController::GetSerialString()
|
||||
{
|
||||
wchar_t serial_string[128];
|
||||
int ret = hid_get_serial_number_string(dev, serial_string, 128);
|
||||
|
||||
if(ret != 0)
|
||||
{
|
||||
return("");
|
||||
}
|
||||
|
||||
return(StringUtils::wstring_to_string(serial_string));
|
||||
}
|
||||
|
||||
void MSIMonitorController::Set(uint8_t mode_value, const std::vector<RGBColor> colors, uint8_t last_bit)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Prepare color data |
|
||||
\*---------------------------------------------------------*/
|
||||
uint8_t data[MSI_MONITOR_PACKET_SIZE];
|
||||
memset(data, 0x00, MSI_MONITOR_PACKET_SIZE);
|
||||
|
||||
unsigned int offset = 0;
|
||||
|
||||
data[offset++] = 0x71;
|
||||
data[offset++] = 0x01;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
data[offset++] = 0x00;
|
||||
}
|
||||
|
||||
data[offset++] = 0x01;
|
||||
data[offset++] = 0x64;
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
data[offset++] = 0x00;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| put mode_value |
|
||||
\*---------------------------------------------------------*/
|
||||
data[offset++] = mode_value;
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
data[offset++] = 0x00;
|
||||
}
|
||||
data[offset++] = 0x01;
|
||||
data[offset++] = 0x64;
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
data[offset++] = 0x00;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*\
|
||||
| this data looks like placeholder for additional LEDs in other monitors |
|
||||
\*-----------------------------------------------------------------------*/
|
||||
for(int i = 0; i < 9; i++)
|
||||
{
|
||||
data[offset++] = 0xff;
|
||||
data[offset++] = 0x00;
|
||||
data[offset++] = 0x00;
|
||||
}
|
||||
|
||||
//RGB values begin
|
||||
for(const RGBColor color: colors)
|
||||
{
|
||||
data[offset++] = RGBGetRValue(color);
|
||||
data[offset++] = RGBGetGValue(color);
|
||||
data[offset++] = RGBGetBValue(color);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------*\
|
||||
| last bit is probably a write to device bit- 0x01 saves to device, 0x00 doesn't. |
|
||||
| For direct mode, bit is set to 0x00, otherwise lights will flicker |
|
||||
\*-------------------------------------------------------------------------------*/
|
||||
|
||||
data[offset++] = last_bit;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Send the data (1 packet) |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
hid_send_feature_report(dev, data, MSI_MONITOR_PACKET_SIZE);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_MSIMonitor.cpp |
|
||||
| |
|
||||
| RGBController for MSI monitor (gaming controller) |
|
||||
| |
|
||||
| Andy Herbert 2026 June 1 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <hidapi.h>
|
||||
#include "RGBController.h"
|
||||
|
||||
#define MSI_MONITOR_LEDS 9
|
||||
#define MSI_MONITOR_PACKET_SIZE 78
|
||||
|
||||
enum
|
||||
{
|
||||
MSI_MONITOR_OFF_MODE_VALUE = 0x00,
|
||||
MSI_MONITOR_STATIC_MODE_VALUE = 0x01,
|
||||
MSI_MONITOR_BREATHING_MODE_VALUE = 0x02,
|
||||
MSI_MONITOR_FLASHING_MODE_VALUE = 0x03,
|
||||
MSI_MONITOR_LIGHTNING_MODE_VALUE = 0x05,
|
||||
MSI_MONITOR_MARQUEE_MODE_VALUE = 0x06,
|
||||
MSI_MONITOR_METEOR_MODE_VALUE = 0x08,
|
||||
MSI_MONITOR_RAINBOW_MODE_VALUE = 0x1A,
|
||||
MSI_MONITOR_RANDOM_MODE_VALUE = 0x1F
|
||||
};
|
||||
|
||||
class MSIMonitorController
|
||||
{
|
||||
public:
|
||||
MSIMonitorController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name);
|
||||
~MSIMonitorController();
|
||||
|
||||
std::string GetDeviceLocation();
|
||||
std::string GetFirmwareVersion();
|
||||
std::string GetNameString();
|
||||
std::string GetSerialString();
|
||||
|
||||
void Set(uint8_t mode_value, const std::vector<RGBColor> colors, uint8_t last_bit);
|
||||
|
||||
private:
|
||||
hid_device *dev;
|
||||
std::string description;
|
||||
std::string location;
|
||||
std::string name;
|
||||
std::string version;
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| MSIKeyboardControllerDetect.cpp |
|
||||
| |
|
||||
| Detector for MSI monitor (MSI Gaming Controller) |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "Detector.h"
|
||||
#include "MSIMonitorController.h"
|
||||
#include "RGBController_MSIMonitor.h"
|
||||
|
||||
#define MSI_USB_VID 0x1462
|
||||
#define MSI_USB_PID 0x3FA4
|
||||
|
||||
/*----------------------------------------------------------*\
|
||||
| |
|
||||
| DetectMSIMonitorController |
|
||||
| |
|
||||
| Detect MSI monitor and maybe others |
|
||||
| |
|
||||
\*----------------------------------------------------------*/
|
||||
|
||||
static void DetectMSIMonitorController(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(dev)
|
||||
{
|
||||
MSIMonitorController* controller = new MSIMonitorController(dev, *info, name);
|
||||
RGBController_MSIMonitor* rgb_controller = new RGBController_MSIMonitor(controller);
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_HID_DETECTOR_IPU("MSI Monitor (Gaming Controller)", DetectMSIMonitorController, MSI_USB_VID, MSI_USB_PID, 0, 0x01, 0);
|
||||
@@ -0,0 +1,171 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_MSIMonitor.cpp |
|
||||
| |
|
||||
| RGBController for MSI monitor (gaming controller) |
|
||||
| |
|
||||
| Andy Herbert 2026 June 1 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "RGBController_MSIMonitor.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
/**------------------------------------------------------------------*\
|
||||
@name MSIMonitor
|
||||
@category Accessory
|
||||
@type USB
|
||||
@save :robot:
|
||||
@direct :white_check_mark:
|
||||
@effects :white_check_mark:
|
||||
@detectors DetectMSIMonitorController
|
||||
@comment Developed with MSI MAG272CQR
|
||||
\*-------------------------------------------------------------------*/
|
||||
RGBController_MSIMonitor::RGBController_MSIMonitor(MSIMonitorController* controller_ptr)
|
||||
{
|
||||
controller = controller_ptr;
|
||||
|
||||
name = controller->GetNameString();
|
||||
vendor = "MSI";
|
||||
type = DEVICE_TYPE_MONITOR;
|
||||
description = "MSI Monitor (Gaming Controller)";
|
||||
location = controller->GetDeviceLocation();
|
||||
serial = controller->GetSerialString();
|
||||
|
||||
mode Direct;
|
||||
Direct.name = "Direct";
|
||||
Direct.value = MSI_MONITOR_STATIC_MODE_VALUE;
|
||||
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
|
||||
Direct.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Direct);
|
||||
|
||||
mode Static;
|
||||
Static.name = "Static";
|
||||
Static.value = MSI_MONITOR_STATIC_MODE_VALUE;
|
||||
Static.flags = MODE_FLAG_HAS_PER_LED_COLOR;
|
||||
Static.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Static);
|
||||
|
||||
|
||||
mode Breathing;
|
||||
Breathing.name = "Breathing";
|
||||
Breathing.value = MSI_MONITOR_BREATHING_MODE_VALUE;
|
||||
Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR;
|
||||
Breathing.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Breathing);
|
||||
|
||||
mode Flashing;
|
||||
Flashing.name = "Flashing";
|
||||
Flashing.value = MSI_MONITOR_FLASHING_MODE_VALUE;
|
||||
Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR;
|
||||
Flashing.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Flashing);
|
||||
|
||||
mode Lightning;
|
||||
Lightning.name = "Lightning";
|
||||
Lightning.value = MSI_MONITOR_LIGHTNING_MODE_VALUE;
|
||||
Lightning.flags = MODE_FLAG_HAS_PER_LED_COLOR;
|
||||
Lightning.color_mode = MODE_COLORS_PER_LED;
|
||||
modes.push_back(Lightning);
|
||||
|
||||
mode Marquee;
|
||||
Marquee.name = "Marquee";
|
||||
Marquee.value = MSI_MONITOR_MARQUEE_MODE_VALUE;
|
||||
Marquee.flags = MODE_FLAG_AUTOMATIC_SAVE;
|
||||
Marquee.color_mode = MODE_COLORS_NONE;
|
||||
modes.push_back(Marquee);
|
||||
|
||||
mode Meteor;
|
||||
Meteor.name = "Meteor";
|
||||
Meteor.value = MSI_MONITOR_METEOR_MODE_VALUE;
|
||||
Meteor.flags = MODE_FLAG_AUTOMATIC_SAVE;
|
||||
Meteor.color_mode = MODE_COLORS_NONE;
|
||||
modes.push_back(Meteor);
|
||||
|
||||
mode Rainbow;
|
||||
Rainbow.name = "Rainbow";
|
||||
Rainbow.value = MSI_MONITOR_RAINBOW_MODE_VALUE;
|
||||
Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE;
|
||||
Rainbow.color_mode = MODE_COLORS_NONE;
|
||||
modes.push_back(Rainbow);
|
||||
|
||||
mode Random;
|
||||
Random.name = "Random";
|
||||
Random.value = MSI_MONITOR_RANDOM_MODE_VALUE;
|
||||
Random.flags = MODE_FLAG_AUTOMATIC_SAVE;
|
||||
Random.color_mode = MODE_COLORS_RANDOM;
|
||||
modes.push_back(Random);
|
||||
|
||||
mode Off;
|
||||
Off.name = "Off";
|
||||
Off.value = MSI_MONITOR_OFF_MODE_VALUE;
|
||||
Off.flags = MODE_FLAG_AUTOMATIC_SAVE;
|
||||
Off.color_mode = MODE_COLORS_NONE;
|
||||
modes.push_back(Off);
|
||||
|
||||
SetupZones();
|
||||
}
|
||||
|
||||
RGBController_MSIMonitor::~RGBController_MSIMonitor()
|
||||
{
|
||||
keepalive_thread_run = 0;
|
||||
keepalive_thread->join();
|
||||
delete keepalive_thread;
|
||||
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void RGBController_MSIMonitor::SetupZones()
|
||||
{
|
||||
zone new_zone;
|
||||
|
||||
new_zone.name = "Rear";
|
||||
new_zone.type = ZONE_TYPE_LINEAR;
|
||||
new_zone.leds_min = 9;
|
||||
new_zone.leds_max = 9;
|
||||
new_zone.leds_count = 9;
|
||||
new_zone.matrix_map = nullptr;
|
||||
|
||||
zones.emplace_back(new_zone);
|
||||
|
||||
for(unsigned int i = 0 ; i < 9; i ++)
|
||||
{
|
||||
led new_led;
|
||||
new_led.name = "LED " + std::to_string(i + 1);
|
||||
leds.push_back(new_led);
|
||||
}
|
||||
|
||||
SetupColors();
|
||||
}
|
||||
|
||||
void RGBController_MSIMonitor::ResizeZone(int /*zone*/, int /*new_size*/)
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| This device does not support resizing zones |
|
||||
\*---------------------------------------------------------*/
|
||||
}
|
||||
|
||||
void RGBController_MSIMonitor::DeviceUpdateLEDs()
|
||||
{
|
||||
last_update_time = std::chrono::steady_clock::now();
|
||||
controller->Set(modes[active_mode].value, colors, active_mode == 0 ? 0x00 : 0x01);
|
||||
}
|
||||
|
||||
void RGBController_MSIMonitor::UpdateZoneLEDs(int /*zone*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_MSIMonitor::UpdateSingleLED(int /*led*/)
|
||||
{
|
||||
DeviceUpdateLEDs();
|
||||
}
|
||||
|
||||
void RGBController_MSIMonitor::DeviceUpdateMode()
|
||||
{
|
||||
controller->Set(modes[active_mode].value, colors, 0x01);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_MSIMonitor.cpp |
|
||||
| |
|
||||
| RGBController for MSI monitor (gaming controller) |
|
||||
| |
|
||||
| Andy Herbert 2026 May 16 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include "RGBController.h"
|
||||
#include "MSIMonitorController.h"
|
||||
|
||||
class RGBController_MSIMonitor : public RGBController
|
||||
{
|
||||
public:
|
||||
RGBController_MSIMonitor(MSIMonitorController* controller_ptr);
|
||||
~RGBController_MSIMonitor();
|
||||
|
||||
void SetupZones();
|
||||
void ResizeZone(int zone, int new_size);
|
||||
void DeviceUpdateLEDs();
|
||||
void UpdateZoneLEDs(int zone);
|
||||
void UpdateSingleLED(int led);
|
||||
void DeviceUpdateMode();
|
||||
|
||||
private:
|
||||
MSIMonitorController* controller;
|
||||
std::thread* keepalive_thread;
|
||||
std::atomic<bool> keepalive_thread_run;
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
|
||||
|
||||
void KeepaliveThread();
|
||||
};
|
||||
+1
@@ -307,6 +307,7 @@ static const mystic_light_185_config board_configs[] =
|
||||
{ 0x7D32, 1, 0, 0, 1, &zones_set10, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG Z690 TOMAHAWK WIFI DDR4
|
||||
{ 0x7D33, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO Z790-VC WIFI
|
||||
{ 0x7D36, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO Z690-P DDR4
|
||||
{ 0x7D37, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B760M-VC WIFI (MS-7D37) - fans on JRAINBOW2
|
||||
{ 0x7D38, 0, 0, 0, 1, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z590 UNIFY-X
|
||||
{ 0x7D40, 0, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B760i EDGE WIFI DDR4
|
||||
{ 0x7D41, 6, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B660M TOMAHAWK WIFI DDR4
|
||||
|
||||
+32
-22
@@ -67,32 +67,42 @@ static const std::string board_names[] =
|
||||
"MSI MAG B850 TOMAHAWK MAX WIFI (MS-7E62)",
|
||||
"MSI PRO B850M-P WIFI (MS-7E71)",
|
||||
"MSI MAG Z890 TOMAHAWK WIFI (MS-7E32)",
|
||||
"MSI MPG B850 EDGE TI WIFI (MS-7E62)"
|
||||
"MSI MPG B850 EDGE TI WIFI (MS-7E62)",
|
||||
"MSI PRO B850M-VC WIFI6E (MS-7E71)",
|
||||
"MSI MAG B850 TOMAHAWK WIFI (MS-7E53)",
|
||||
"MSI MEG Z890 UNIFY-X (MS-7E20)",
|
||||
"MSI PRO X870E-S EVO WIFI (MS-7E86)",
|
||||
"MSI PRO Z890-P WIFI (MS-7E34)",
|
||||
};
|
||||
|
||||
static const mystic_light_761_config board_configs[] =
|
||||
{
|
||||
{ &(board_names[0]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI X870 TOMAHAWK WIFI
|
||||
{ &(board_names[1]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG B850M MORTAR WIFI
|
||||
{ &(board_names[2]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG B850I EDGE TI WIFI
|
||||
{ &(board_names[3]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI X870 GAMING PLUS WIFI
|
||||
{ &(board_names[4]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850 GAMING PLUS WIFI6E
|
||||
{ &(board_names[5]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850M GAMING PLUS WIFI6E
|
||||
{ &(board_names[6]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG X870E CARBON WIFI
|
||||
{ &(board_names[7]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI Z890 GAMING PLUS WIFI
|
||||
{ &(board_names[8]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI X870E GAMING PLUS WIFI
|
||||
{ &(board_names[9]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG X870E TOMAHAWK WIFI
|
||||
{ &(board_names[10]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850-P WIFI (MS-7E56)
|
||||
{ &(board_names[11]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850M GAMING PLUS WIFI
|
||||
{ &(board_names[12]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO X870E-P WIFI
|
||||
{ &(board_names[13]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG X870I EDGE TI EVO WIFI (MS-7E50)
|
||||
{ &(board_names[14]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850 GAMING PLUS WIFI (MS-7E56)
|
||||
{ &(board_names[15]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO X870-P WIFI
|
||||
{ &(board_names[16]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG X870E EDGE TI WIFI
|
||||
{ &(board_names[17]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG B850 TOMAHAWK MAX WIFI
|
||||
{ &(board_names[18]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850M-P WIFI (MS-7E71)
|
||||
{ &(board_names[19]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG Z890 TOMAHAWK WIFI (MS-7E32)
|
||||
{ &(board_names[20]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG B850 EDGE TI WIFI (MS-7E62)
|
||||
{ &(board_names[0]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI X870 TOMAHAWK WIFI
|
||||
{ &(board_names[1]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG B850M MORTAR WIFI
|
||||
{ &(board_names[2]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG B850I EDGE TI WIFI
|
||||
{ &(board_names[3]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI X870 GAMING PLUS WIFI
|
||||
{ &(board_names[4]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850 GAMING PLUS WIFI6E
|
||||
{ &(board_names[5]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850M GAMING PLUS WIFI6E
|
||||
{ &(board_names[6]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG X870E CARBON WIFI
|
||||
{ &(board_names[7]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI Z890 GAMING PLUS WIFI
|
||||
{ &(board_names[8]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI X870E GAMING PLUS WIFI
|
||||
{ &(board_names[9]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG X870E TOMAHAWK WIFI
|
||||
{ &(board_names[10]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850-P WIFI (MS-7E56)
|
||||
{ &(board_names[11]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850M GAMING PLUS WIFI
|
||||
{ &(board_names[12]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO X870E-P WIFI
|
||||
{ &(board_names[13]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG X870I EDGE TI EVO WIFI (MS-7E50)
|
||||
{ &(board_names[14]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850 GAMING PLUS WIFI (MS-7E56)
|
||||
{ &(board_names[15]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO X870-P WIFI
|
||||
{ &(board_names[16]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG X870E EDGE TI WIFI
|
||||
{ &(board_names[17]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG B850 TOMAHAWK MAX WIFI
|
||||
{ &(board_names[18]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850M-P WIFI (MS-7E71)
|
||||
{ &(board_names[19]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG Z890 TOMAHAWK WIFI (MS-7E32)
|
||||
{ &(board_names[20]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG B850 EDGE TI WIFI (MS-7E62)
|
||||
{ &(board_names[21]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850M-VC WIFI6E (MS-7E71)
|
||||
{ &(board_names[22]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG B850 TOMAHAWK WIFI (MS-7E53)
|
||||
{ &(board_names[23]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MEG Z890 UNIFY-X (MS-7E20)
|
||||
{ &(board_names[24]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO X870E-S EVO WIFI (MS-7E86)
|
||||
{ &(board_names[25]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO Z890-P WIFI (MS-7E34)
|
||||
};
|
||||
|
||||
enum MSI_ZONE setup_map [] =
|
||||
|
||||
@@ -209,6 +209,7 @@ REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D31", DetectMSIMysticLightCont
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D32", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D32, 0x0001, 0x00);
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D33", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D33, 0x0001, 0x00);
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D36", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D36, 0x0001, 0x00);
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D37", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D37, 0x0001, 0x00);
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D38", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D38, 0x0001, 0x00);
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D40", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D40, 0x0001, 0x00);
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D41", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D41, 0x0001, 0x00);
|
||||
@@ -253,6 +254,7 @@ REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E80", DetectMSIMysticLightCont
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E81", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E81, 0x0001, 0x00);
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E34", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E34, 0x0001, 0x00);
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E32", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E32, 0x0001, 0x00);
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E20", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E20, 0x0001, 0x00);
|
||||
// Detector for the set of common boards
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light Common", DetectMSIMysticLightControllers, MSI_USB_VID_COMMON, MSI_USB_PID_COMMON, 0x0001, 0x00);
|
||||
REGISTER_HID_DETECTOR_PU("MSI Mystic Light X870", DetectMSIMysticLightControllers, MSI_USB_VID_COMMON, MSI_USB_PID_COMMON, 0xFF00, 0x01);
|
||||
|
||||
@@ -17,12 +17,13 @@
|
||||
#include "NZXTHue2Controller.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
NZXTHue2Controller::NZXTHue2Controller(hid_device* dev_handle, unsigned int rgb_channels, unsigned int fan_channels, const char* path, std::string dev_name)
|
||||
NZXTHue2Controller::NZXTHue2Controller(hid_device* dev_handle, unsigned int rgb_channels, unsigned int fan_channels, const char* path, std::string dev_name, bool use_2023_effects_val)
|
||||
{
|
||||
dev = dev_handle;
|
||||
location = path;
|
||||
name = dev_name;
|
||||
|
||||
use_2023_effects = use_2023_effects_val;
|
||||
num_fan_channels = fan_channels;
|
||||
num_rgb_channels = rgb_channels;
|
||||
|
||||
@@ -330,7 +331,14 @@ void NZXTHue2Controller::SetChannelEffect
|
||||
/*-----------------------------------------------------*\
|
||||
| Send effect packet |
|
||||
\*-----------------------------------------------------*/
|
||||
SendEffect(channel, mode, speed, direction, num_colors, &color_data[0]);
|
||||
if(use_2023_effects)
|
||||
{
|
||||
SendEffect2023(channel, mode, speed, direction, num_colors, &color_data[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
SendEffect(channel, mode, speed, direction, num_colors, &color_data[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void NZXTHue2Controller::SetChannelLEDs
|
||||
@@ -357,14 +365,15 @@ void NZXTHue2Controller::SetChannelLEDs
|
||||
/*-----------------------------------------------------*\
|
||||
| Send first group of color data |
|
||||
\*-----------------------------------------------------*/
|
||||
SendDirect(channel, 0, 20, &color_data[0]);
|
||||
unsigned char first_color_count = (num_colors > 20) ? 20 : (unsigned char)num_colors;
|
||||
SendDirect(channel, 0, first_color_count, &color_data[0]);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Send second group of color data if necessary |
|
||||
\*-----------------------------------------------------*/
|
||||
if(num_colors > 20)
|
||||
{
|
||||
SendDirect(channel, 1, 20, &color_data[60]);
|
||||
SendDirect(channel, 1, (unsigned char)(num_colors - 20), &color_data[60]);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
@@ -502,6 +511,99 @@ void NZXTHue2Controller::SendEffect
|
||||
//hid_read(dev, usb_buf, 64);
|
||||
}
|
||||
|
||||
void NZXTHue2Controller::SendEffect2023
|
||||
(
|
||||
unsigned char channel,
|
||||
unsigned char mode,
|
||||
unsigned char speed,
|
||||
bool direction,
|
||||
unsigned char color_count,
|
||||
unsigned char* color_data
|
||||
)
|
||||
{
|
||||
unsigned char usb_buf[64];
|
||||
unsigned char speed_data[2] = { 0x32, 0x00 };
|
||||
unsigned char mode_modifier = 0x00;
|
||||
|
||||
memset(usb_buf, 0x00, sizeof(usb_buf));
|
||||
|
||||
if(speed > HUE_2_SPEED_FASTEST)
|
||||
{
|
||||
speed = HUE_2_SPEED_NORMAL;
|
||||
}
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case HUE_2_MODE_FADING:
|
||||
{
|
||||
const unsigned char values[5][2] = { {0x50, 0x00}, {0x3C, 0x00}, {0x28, 0x00}, {0x14, 0x00}, {0x0A, 0x00} };
|
||||
speed_data[0] = values[speed][0];
|
||||
speed_data[1] = values[speed][1];
|
||||
mode_modifier = 0x08;
|
||||
}
|
||||
break;
|
||||
|
||||
case HUE_2_MODE_SPECTRUM:
|
||||
case HUE_2_MODE_RAINBOW_FLOW:
|
||||
case HUE_2_MODE_SUPER_RAINBOW:
|
||||
case HUE_2_MODE_RAINBOW_PULSE:
|
||||
{
|
||||
const unsigned char values[5][2] = { {0x5E, 0x01}, {0x2C, 0x01}, {0xFA, 0x00}, {0x96, 0x00}, {0x50, 0x00} };
|
||||
speed_data[0] = values[speed][0];
|
||||
speed_data[1] = values[speed][1];
|
||||
}
|
||||
break;
|
||||
|
||||
case HUE_2_MODE_ALTERNATING:
|
||||
{
|
||||
const unsigned char values[5][2] = { {0x40, 0x06}, {0x14, 0x05}, {0xE8, 0x03}, {0x20, 0x03}, {0x58, 0x02} };
|
||||
speed_data[0] = values[speed][0];
|
||||
speed_data[1] = values[speed][1];
|
||||
}
|
||||
break;
|
||||
|
||||
case HUE_2_MODE_PULSING:
|
||||
case HUE_2_MODE_STARRY_NIGHT:
|
||||
{
|
||||
const unsigned char values[5][2] = { {0x19, 0x00}, {0x14, 0x00}, {0x0F, 0x00}, {0x07, 0x00}, {0x04, 0x00} };
|
||||
speed_data[0] = values[speed][0];
|
||||
speed_data[1] = values[speed][1];
|
||||
mode_modifier = (mode == HUE_2_MODE_PULSING) ? 0x08 : 0x00;
|
||||
}
|
||||
break;
|
||||
|
||||
case HUE_2_MODE_BREATHING:
|
||||
{
|
||||
const unsigned char values[5][2] = { {0x28, 0x00}, {0x1E, 0x00}, {0x14, 0x00}, {0x0A, 0x00}, {0x04, 0x00} };
|
||||
speed_data[0] = values[speed][0];
|
||||
speed_data[1] = values[speed][1];
|
||||
mode_modifier = 0x08;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
usb_buf[0x00] = 0x2A;
|
||||
usb_buf[0x01] = 0x04;
|
||||
usb_buf[0x02] = (unsigned char)(1 << channel);
|
||||
usb_buf[0x03] = usb_buf[0x02];
|
||||
usb_buf[0x04] = mode;
|
||||
usb_buf[0x05] = speed_data[0];
|
||||
usb_buf[0x06] = speed_data[1];
|
||||
|
||||
memcpy(&usb_buf[0x07], color_data, color_count * 3);
|
||||
|
||||
usb_buf[0x37] = direction ? 0x02 : 0x00;
|
||||
usb_buf[0x38] = color_count;
|
||||
usb_buf[0x39] = mode_modifier;
|
||||
usb_buf[0x3A] = 0x08;
|
||||
usb_buf[0x3B] = 0x03;
|
||||
|
||||
hid_write(dev, usb_buf, 64);
|
||||
}
|
||||
|
||||
void NZXTHue2Controller::SendFirmwareRequest()
|
||||
{
|
||||
unsigned char usb_buf[64];
|
||||
|
||||
@@ -66,7 +66,7 @@ enum
|
||||
class NZXTHue2Controller
|
||||
{
|
||||
public:
|
||||
NZXTHue2Controller(hid_device* dev_handle, unsigned int rgb_channels, unsigned int fan_channels, const char* path, std::string dev_name);
|
||||
NZXTHue2Controller(hid_device* dev_handle, unsigned int rgb_channels, unsigned int fan_channels, const char* path, std::string dev_name, bool use_2023_effects = false);
|
||||
~NZXTHue2Controller();
|
||||
|
||||
std::string GetFirmwareVersion();
|
||||
@@ -129,6 +129,7 @@ private:
|
||||
char firmware_version[16];
|
||||
std::string location;
|
||||
std::string name;
|
||||
bool use_2023_effects;
|
||||
unsigned int num_fan_channels;
|
||||
unsigned int num_rgb_channels;
|
||||
|
||||
@@ -155,5 +156,15 @@ private:
|
||||
unsigned char* color_data
|
||||
);
|
||||
|
||||
void SendEffect2023
|
||||
(
|
||||
unsigned char channel,
|
||||
unsigned char mode,
|
||||
unsigned char speed,
|
||||
bool direction,
|
||||
unsigned char color_count,
|
||||
unsigned char* color_data
|
||||
);
|
||||
|
||||
void SendFirmwareRequest();
|
||||
};
|
||||
|
||||
@@ -38,13 +38,13 @@
|
||||
#define NZXT_SMART_DEVICE_V2_1_PID 0x200D
|
||||
#define NZXT_SMART_DEVICE_V2_2_PID 0x200F
|
||||
|
||||
static void spawn_hue(hid_device_info* info, const std::string& name, int rgb_channels, int fan_channels)
|
||||
static void spawn_hue(hid_device_info* info, const std::string& name, int rgb_channels, int fan_channels, bool use_2023_effects = false)
|
||||
{
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(dev)
|
||||
{
|
||||
NZXTHue2Controller* controller = new NZXTHue2Controller(dev, rgb_channels, fan_channels, info->path, name);
|
||||
NZXTHue2Controller* controller = new NZXTHue2Controller(dev, rgb_channels, fan_channels, info->path, name, use_2023_effects);
|
||||
RGBController_NZXTHue2* rgb_controller = new RGBController_NZXTHue2(controller);
|
||||
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
@@ -83,7 +83,7 @@ void DetectNZXTKrakenX3(hid_device_info* info, const std::string& name)
|
||||
|
||||
void DetectNZXTKrakenElite(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
spawn_hue(info, name, 2, 2);
|
||||
spawn_hue(info, name, 2, 2, true);
|
||||
}
|
||||
|
||||
void DetectNZXTFanController(hid_device_info* info, const std::string& name)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*---------------------------------------------------------*\
|
||||
/*---------------------------------------------------------*\
|
||||
| NollieController.cpp |
|
||||
| |
|
||||
| Driver for Nollie |
|
||||
@@ -82,7 +82,7 @@ void NollieController::SetMos(bool mos)
|
||||
|
||||
void NollieController::SetChannelLEDs(unsigned char channel, RGBColor* colors, unsigned int num_colors)
|
||||
{
|
||||
if(usb_pid == NOLLIE32_PID || usb_pid == NOLLIE16_PID)
|
||||
if(usb_pid == NOLLIE32_PID || usb_pid == NOLLIE16_PID || usb_pid == NOLLIE32_OS21_PID || usb_pid == NOLLIE16_OS21_PID)
|
||||
{
|
||||
SendPacket(channel,&colors[0], num_colors);
|
||||
}
|
||||
@@ -162,9 +162,12 @@ void NollieController::SendPacketFS(unsigned char channel,unsigned char packet_i
|
||||
packet_interval = 2;
|
||||
break;
|
||||
case NOLLIE8_PID:
|
||||
case NOLLIE8_OS21_PID:
|
||||
case PRISM8_OS21_PID:
|
||||
packet_interval = 6;
|
||||
break;
|
||||
case NOLLIE1_PID:
|
||||
case NOLLIE1_OS21_PID:
|
||||
packet_interval = 30;
|
||||
break;
|
||||
default:
|
||||
@@ -179,7 +182,7 @@ void NollieController::SendPacketFS(unsigned char channel,unsigned char packet_i
|
||||
usb_buf[0x02 + (color_idx * 3)] = RGBGetRValue(colors[color_idx]);
|
||||
usb_buf[0x03 + (color_idx * 3)] = RGBGetGValue(colors[color_idx]);
|
||||
usb_buf[0x04 + (color_idx * 3)] = RGBGetBValue(colors[color_idx]);
|
||||
if(dev_pid == NOLLIE8_PID || dev_pid == NOLLIE1_PID )
|
||||
if(dev_pid == NOLLIE8_PID || dev_pid == NOLLIE1_PID || dev_pid == NOLLIE8_OS21_PID || dev_pid == PRISM8_OS21_PID || dev_pid == NOLLIE1_OS21_PID)
|
||||
{
|
||||
usb_buf[0x02 + (color_idx * 3)] = RGBGetGValue(colors[color_idx]);
|
||||
usb_buf[0x03 + (color_idx * 3)] = RGBGetRValue(colors[color_idx]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*---------------------------------------------------------*\
|
||||
/*---------------------------------------------------------*\
|
||||
| NollieController.h |
|
||||
| |
|
||||
| Driver for Nollie |
|
||||
@@ -26,18 +26,23 @@
|
||||
|
||||
#define NOLLIE32_CHANNELS_NUM 32
|
||||
#define NOLLIE32_PID 0x4714
|
||||
#define NOLLIE32_OS21_PID 0x2A32
|
||||
#define NOLLIE32_VID 0x3061
|
||||
|
||||
#define NOLLIE16_CHANNELS_NUM 16
|
||||
#define NOLLIE16_PID 0x4716
|
||||
#define NOLLIE16_OS21_PID 0x2A16
|
||||
#define NOLLIE16_VID 0x3061
|
||||
|
||||
#define NOLLIE8_CHANNELS_NUM 8
|
||||
#define NOLLIE8_PID 0x1F01
|
||||
#define NOLLIE8_OS21_PID 0x2A08
|
||||
#define PRISM8_OS21_PID 0x2C08
|
||||
#define NOLLIE8_VID 0x16D2
|
||||
|
||||
#define NOLLIE1_CHANNELS_NUM 1
|
||||
#define NOLLIE1_PID 0x1F11
|
||||
#define NOLLIE1_OS21_PID 0x2A01
|
||||
#define NOLLIE1_VID 0x16D2
|
||||
|
||||
#define NOLLIE28_12_CHANNELS_NUM 12
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*---------------------------------------------------------*\
|
||||
/*---------------------------------------------------------*\
|
||||
| NollieControllerDetect.cpp |
|
||||
| |
|
||||
| Detector for Nollie |
|
||||
@@ -16,6 +16,18 @@
|
||||
|
||||
void DetectNollieControllers(hid_device_info* info, const std::string& name)
|
||||
{
|
||||
if((info->product_id == NOLLIE1_OS21_PID || info->product_id == NOLLIE8_OS21_PID || info->product_id == PRISM8_OS21_PID)
|
||||
&& info->interface_number != 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if((info->product_id == NOLLIE16_OS21_PID || info->product_id == NOLLIE32_OS21_PID)
|
||||
&& info->interface_number != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hid_device* dev = hid_open_path(info->path);
|
||||
|
||||
if(dev)
|
||||
@@ -37,6 +49,11 @@ REGISTER_HID_DETECTOR("Nollie 32CH", DetectNollieControllers, NOLLIE32_VID, NOLL
|
||||
REGISTER_HID_DETECTOR("Nollie 16CH", DetectNollieControllers, NOLLIE16_VID, NOLLIE16_PID);
|
||||
REGISTER_HID_DETECTOR("Nollie 8CH", DetectNollieControllers, NOLLIE8_VID, NOLLIE8_PID);
|
||||
REGISTER_HID_DETECTOR("Nollie 1CH", DetectNollieControllers, NOLLIE1_VID, NOLLIE1_PID);
|
||||
REGISTER_HID_DETECTOR("Nollie 32_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE32_OS21_PID);
|
||||
REGISTER_HID_DETECTOR("Nollie 16_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE16_OS21_PID);
|
||||
REGISTER_HID_DETECTOR("Nollie 8_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE8_OS21_PID);
|
||||
REGISTER_HID_DETECTOR("Prism 8_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, PRISM8_OS21_PID);
|
||||
REGISTER_HID_DETECTOR("Nollie 1_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE1_OS21_PID);
|
||||
REGISTER_HID_DETECTOR("Nollie 28 12", DetectNollieControllers, NOLLIE28_12_VID, NOLLIE28_12_PID);
|
||||
REGISTER_HID_DETECTOR("Nollie 28 L1", DetectNollieControllers, NOLLIE28_12_VID, NOLLIE28_L1_PID);
|
||||
REGISTER_HID_DETECTOR("Nollie 28 L2", DetectNollieControllers, NOLLIE28_12_VID, NOLLIE28_L2_PID);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*---------------------------------------------------------*\
|
||||
/*---------------------------------------------------------*\
|
||||
| RGBController_Nollie.cpp |
|
||||
| |
|
||||
| RGBController for Nollie |
|
||||
@@ -64,11 +64,13 @@ void RGBController_Nollie::SetupZones()
|
||||
switch(controller->GetUSBPID())
|
||||
{
|
||||
case NOLLIE32_PID:
|
||||
case NOLLIE32_OS21_PID:
|
||||
channels_num = NOLLIE32_CHANNELS_NUM;
|
||||
ch_led_num = NOLLIE_HS_CH_LED_NUM;
|
||||
channel_index = ch32;
|
||||
break;
|
||||
case NOLLIE16_PID:
|
||||
case NOLLIE16_OS21_PID:
|
||||
channels_num = NOLLIE16_CHANNELS_NUM;
|
||||
ch_led_num = NOLLIE_HS_CH_LED_NUM;
|
||||
channel_index = ch16;
|
||||
@@ -80,10 +82,13 @@ void RGBController_Nollie::SetupZones()
|
||||
ch_led_num = NOLLIE_12_CH_LED_NUM;
|
||||
break;
|
||||
case NOLLIE8_PID:
|
||||
case NOLLIE8_OS21_PID:
|
||||
case PRISM8_OS21_PID:
|
||||
channels_num = NOLLIE8_CHANNELS_NUM;
|
||||
ch_led_num = NOLLIE_8_CH_LED_NUM;
|
||||
break;
|
||||
case NOLLIE1_PID:
|
||||
case NOLLIE1_OS21_PID:
|
||||
channels_num = NOLLIE1_CHANNELS_NUM;
|
||||
ch_led_num = NOLLIE_1_CH_LED_NUM;
|
||||
break;
|
||||
@@ -171,7 +176,7 @@ void RGBController_Nollie::ResizeZone(int zone, int new_size)
|
||||
/*-----------------------------------------------------*\
|
||||
| Nollie1 needs to report the number of LEDs |
|
||||
\*-----------------------------------------------------*/
|
||||
if(controller->GetUSBVID() == NOLLIE1_VID && controller->GetUSBPID() == NOLLIE1_PID)
|
||||
if(controller->GetUSBPID() == NOLLIE1_PID)
|
||||
{
|
||||
controller->InitChLEDs(&new_size,NOLLIE1_CHANNELS_NUM);
|
||||
}
|
||||
@@ -192,7 +197,7 @@ void RGBController_Nollie::ResizeZone(int zone, int new_size)
|
||||
void RGBController_Nollie::DeviceUpdateLEDs()
|
||||
{
|
||||
unsigned int DevPid = controller->GetUSBPID();
|
||||
if(DevPid == NOLLIE32_PID || DevPid == NOLLIE16_PID)
|
||||
if(DevPid == NOLLIE32_PID || DevPid == NOLLIE16_PID || DevPid == NOLLIE32_OS21_PID || DevPid == NOLLIE16_OS21_PID)
|
||||
{
|
||||
std::vector<int> ChSort;
|
||||
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
|
||||
|
||||
@@ -39,6 +39,7 @@ void DetectPNYARGBEpicXGPULargeControllers(i2c_smbus_interface* bus, uint8_t i2c
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5060Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, PNY_SUB_VEN, PNY_RTX_5060TI_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
|
||||
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070 ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, PNY_SUB_VEN, PNY_RTX_5070_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
|
||||
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PNY_SUB_VEN, PNY_RTX_5070TI_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
|
||||
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5080 ARGB Epic-X OC", DetectPNYARGBEpicXGPULargeControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, PNY_SUB_VEN, PNY_RTX_5080_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
|
||||
|
||||
@@ -0,0 +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? */
|
||||
} qmk_rgb_matrix_led_info;
|
||||
@@ -0,0 +1,533 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| QMKKeychronController.cpp |
|
||||
| |
|
||||
| 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 |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <string.h>
|
||||
#include "hsv.h"
|
||||
#include "QMKKeychronController.h"
|
||||
#include "QMKViaCommands.h"
|
||||
#include "StringUtils.h"
|
||||
#include "LogManager.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char *path)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Initialize controller fields |
|
||||
\*-----------------------------------------------------*/
|
||||
dev = dev_handle;
|
||||
location = path;
|
||||
kc_protocol_version = 0;
|
||||
supported_features = 0;
|
||||
via_protocol_version = 0;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| 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 protocol version |
|
||||
\*-----------------------------------------------------*/
|
||||
CmdGetViaProtocolVersion(&via_protocol_version);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get Keychron protocol version |
|
||||
\*-----------------------------------------------------*/
|
||||
CmdGetKeychronProtocolVersion(&kc_protocol_version);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get Keychron firmware version |
|
||||
\*-----------------------------------------------------*/
|
||||
kc_firmware_version = CmdGetKeychronFirmwareVersion();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get supported Keychron features |
|
||||
\*-----------------------------------------------------*/
|
||||
CmdGetSupportFeature(&supported_features);
|
||||
|
||||
if(!GetSupported())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get Keychron RGB protocol version |
|
||||
\*-----------------------------------------------------*/
|
||||
CmdGetKeychronRGBProtocolVersion(&kc_rgb_protocol_version);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get count of LEDs |
|
||||
\*-----------------------------------------------------*/
|
||||
CmdGetNumberLEDs(&number_leds);
|
||||
|
||||
led_info.resize(number_leds);
|
||||
keycodes.resize(number_leds);
|
||||
|
||||
for(std::size_t led_idx = 0; led_idx < led_info.size(); led_idx++)
|
||||
{
|
||||
led_info[led_idx].valid = false;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get info and keycode for all LEDs |
|
||||
\*-----------------------------------------------------*/
|
||||
for(unsigned char row = 0; row < 32; row++)
|
||||
{
|
||||
std::vector<unsigned char> row_leds = CmdGetLEDIndexByRow(row);
|
||||
|
||||
for(unsigned char col = 0; col < row_leds.size(); col++)
|
||||
{
|
||||
if(row_leds[col] != 0xFF && row_leds[col] < led_info.size() && led_info[row_leds[col]].valid == false)
|
||||
{
|
||||
led_info[row_leds[col]].valid = true;
|
||||
led_info[row_leds[col]].col = col;
|
||||
led_info[row_leds[col]].row = row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned short led_index = 0; led_index < number_leds; led_index++)
|
||||
{
|
||||
keycodes[led_index] = CmdGetKeycode(0, led_info[led_index].row, led_info[led_index].col);
|
||||
}
|
||||
}
|
||||
|
||||
QMKKeychronController::~QMKKeychronController()
|
||||
{
|
||||
hid_close(dev);
|
||||
}
|
||||
|
||||
std::string QMKKeychronController::GetLocation()
|
||||
{
|
||||
return("HID: " + location);
|
||||
}
|
||||
|
||||
std::string QMKKeychronController::GetName()
|
||||
{
|
||||
return(name);
|
||||
}
|
||||
|
||||
std::string QMKKeychronController::GetSerial()
|
||||
{
|
||||
return(serial);
|
||||
}
|
||||
|
||||
std::string QMKKeychronController::GetVendor()
|
||||
{
|
||||
return(vendor);
|
||||
}
|
||||
|
||||
std::string QMKKeychronController::GetVersion()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Format multi-line version text |
|
||||
\*-----------------------------------------------------*/
|
||||
return("VIA: " + std::to_string(via_protocol_version) + "\r\n" +
|
||||
"Keychron: " + std::to_string(kc_protocol_version) + "\r\n" +
|
||||
"Keychron RGB: " + std::to_string(kc_rgb_protocol_version) + "\r\n" +
|
||||
"Keychron FW: " + kc_firmware_version);
|
||||
}
|
||||
|
||||
bool QMKKeychronController::GetSupported()
|
||||
{
|
||||
return(supported_features & KC_FEATURE_KEYCHRON_RGB);
|
||||
}
|
||||
|
||||
unsigned short QMKKeychronController::GetKeycode(unsigned short led_index)
|
||||
{
|
||||
return(keycodes[led_index]);
|
||||
}
|
||||
|
||||
unsigned short QMKKeychronController::GetLEDCount()
|
||||
{
|
||||
return(number_leds);
|
||||
}
|
||||
|
||||
qmk_rgb_matrix_led_info QMKKeychronController::GetLEDInfo(unsigned short led_index)
|
||||
{
|
||||
return(led_info[led_index]);
|
||||
}
|
||||
|
||||
void QMKKeychronController::SaveMode()
|
||||
{
|
||||
CmdSaveMode();
|
||||
}
|
||||
|
||||
void QMKKeychronController::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 QMKKeychronController::SetMode(unsigned short mode, unsigned char speed, unsigned char hue, unsigned char sat, unsigned char val)
|
||||
{
|
||||
if(mode == 0xFFFF)
|
||||
{
|
||||
CmdSetRGBMatrixMode(KEYCHRON_QHE_PER_KEY_RGB_EFFECT);
|
||||
CmdSetPerKeyRGBType(KEYCHRON_PER_KEY_RGB_SOLID);
|
||||
}
|
||||
else
|
||||
{
|
||||
CmdSetRGBMatrixMode((unsigned char)mode);
|
||||
CmdSetColorHS(hue, sat);
|
||||
CmdSetBrightness(val);
|
||||
CmdSetSpeed(speed);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short QMKKeychronController::CmdGetKeycode
|
||||
(
|
||||
unsigned char layer,
|
||||
unsigned char row,
|
||||
unsigned char col
|
||||
)
|
||||
{
|
||||
unsigned char args[3];
|
||||
unsigned char response[5];
|
||||
unsigned short keycode;
|
||||
|
||||
args[0] = layer;
|
||||
args[1] = row;
|
||||
args[2] = col;
|
||||
|
||||
ViaSendCommand(QMK_VIA_CMD_VIA_DYNAMIC_KEYMAP_GET_KEYCODE, args, sizeof(args), response, sizeof(response));
|
||||
|
||||
keycode = ( response[3] << 8 )| response[4];
|
||||
|
||||
return(keycode);
|
||||
}
|
||||
|
||||
|
||||
std::string QMKKeychronController::CmdGetKeychronFirmwareVersion()
|
||||
{
|
||||
char response[30];
|
||||
|
||||
ViaSendCommand(KC_GET_FIRMWARE_VERSION, NULL, 0, (unsigned char*)response, sizeof(response));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Ensure response null termination |
|
||||
\*-----------------------------------------------------*/
|
||||
response[29] = 0;
|
||||
|
||||
return(std::string(response));
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdGetKeychronProtocolVersion
|
||||
(
|
||||
unsigned char* kc_protocol_version
|
||||
)
|
||||
{
|
||||
ViaSendCommand(KC_GET_PROTOCOL_VERSION, NULL, 0, (unsigned char*)kc_protocol_version, sizeof(unsigned char));
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdGetKeychronRGBProtocolVersion(unsigned short* kc_rgb_protocol_version)
|
||||
{
|
||||
ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_PROTOCOL_VER, NULL, 0, (unsigned char*)kc_rgb_protocol_version, sizeof(unsigned short));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| The RGB protocol version byte order is reversed |
|
||||
\*-----------------------------------------------------*/
|
||||
*kc_rgb_protocol_version = ((*kc_rgb_protocol_version & 0x00FF) << 8) | ((*kc_rgb_protocol_version & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> QMKKeychronController::CmdGetLEDIndexByRow(unsigned char row)
|
||||
{
|
||||
unsigned char args[4];
|
||||
unsigned char response[KEYCHRON_QHE_PACKET_SIZE - 2];
|
||||
|
||||
args[0] = row;
|
||||
args[1] = 0xFF;
|
||||
args[2] = 0xFF;
|
||||
args[3] = 0xFF;
|
||||
|
||||
int bytes_read = ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_LED_IDX, args, sizeof(args), response, sizeof(response));
|
||||
|
||||
std::vector<unsigned char> result;
|
||||
|
||||
if(bytes_read > 0)
|
||||
{
|
||||
for(int i = 1; i < bytes_read; i++)
|
||||
{
|
||||
result.push_back(response[i] == 0xFF ? -1 : response[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdGetNumberLEDs
|
||||
(
|
||||
unsigned short* number_leds
|
||||
)
|
||||
{
|
||||
ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_LED_COUNT, NULL, 0, (unsigned char*)number_leds, sizeof(unsigned short));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| The LED count byte order is reversed |
|
||||
\*-----------------------------------------------------*/
|
||||
*number_leds = ((*number_leds & 0x00FF) << 8) | ((*number_leds & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdGetSupportFeature(unsigned short* supported_features)
|
||||
{
|
||||
ViaSendCommand(KC_GET_SUPPORT_FEATURE, NULL, 0, (unsigned char*)supported_features, sizeof(unsigned short));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| The supported features byte order is reversed |
|
||||
\*-----------------------------------------------------*/
|
||||
*supported_features = ((*supported_features & 0x00FF) << 8) | ((*supported_features & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdGetViaProtocolVersion
|
||||
(
|
||||
unsigned short* via_protocol_version
|
||||
)
|
||||
{
|
||||
ViaSendCommand(QMK_VIA_CMD_GET_PROTOCOL_VERSION, NULL, 0, (unsigned char*)via_protocol_version, sizeof(unsigned short));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| The protocol version byte order is reversed |
|
||||
\*-----------------------------------------------------*/
|
||||
*via_protocol_version = ((*via_protocol_version & 0x00FF) << 8) | ((*via_protocol_version & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdSaveMode()
|
||||
{
|
||||
ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_SAVE, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdSendLEDs(unsigned char start_index, unsigned char number_leds, RGBColor* color_data)
|
||||
{
|
||||
unsigned char args[KEYCHRON_QHE_PACKET_SIZE - 2];
|
||||
|
||||
args[0] = start_index;
|
||||
args[1] = 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);
|
||||
|
||||
args[2 + (led_index * 3)] = (unsigned char)((float)hsv_color.hue * (256.0f / 360.0f));
|
||||
args[3 + (led_index * 3)] = hsv_color.saturation;
|
||||
args[4 + (led_index * 3)] = hsv_color.value;
|
||||
}
|
||||
|
||||
ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_PER_KEY_SET_COLOR, args, sizeof(args), NULL, 0);
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdSetBrightness(unsigned char brightness)
|
||||
{
|
||||
unsigned char args[3];
|
||||
|
||||
args[0] = QMK_VIA_RGB_MATRIX_CHANNEL;
|
||||
args[1] = QMK_VIA_RGB_MATRIX_BRIGHTNESS;
|
||||
args[2] = brightness;
|
||||
|
||||
ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0);
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdSetColorHS(unsigned char h, unsigned char s)
|
||||
{
|
||||
unsigned char args[4];
|
||||
|
||||
args[0] = QMK_VIA_RGB_MATRIX_CHANNEL;
|
||||
args[1] = QMK_VIA_RGB_MATRIX_COLOR;
|
||||
args[2] = h;
|
||||
args[3] = s;
|
||||
|
||||
ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0);
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdSetPerKeyRGBType(unsigned char type)
|
||||
{
|
||||
unsigned char args[1];
|
||||
|
||||
args[0] = type;
|
||||
|
||||
ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_PER_KEY_SET_TYPE, args, sizeof(args), NULL, 0);
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdSetRGBMatrixMode(unsigned char mode)
|
||||
{
|
||||
unsigned char args[3];
|
||||
|
||||
args[0] = QMK_VIA_RGB_MATRIX_CHANNEL;
|
||||
args[1] = QMK_VIA_RGB_MATRIX_EFFECT;
|
||||
args[2] = mode;
|
||||
|
||||
ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0);
|
||||
}
|
||||
|
||||
void QMKKeychronController::CmdSetSpeed(unsigned char speed)
|
||||
{
|
||||
unsigned char args[3];
|
||||
|
||||
args[0] = QMK_VIA_RGB_MATRIX_CHANNEL;
|
||||
args[1] = QMK_VIA_RGB_MATRIX_EFFECT_SPEED;
|
||||
args[2] = speed;
|
||||
|
||||
ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0);
|
||||
}
|
||||
|
||||
int QMKKeychronController::ViaSendCommand
|
||||
(
|
||||
unsigned char cmd,
|
||||
unsigned char* data_in,
|
||||
unsigned char data_in_size,
|
||||
unsigned char* data_out,
|
||||
unsigned char data_out_size
|
||||
)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Standard VIA command with no sub-command |
|
||||
| |
|
||||
| Byte 0: Command |
|
||||
| Byte 1+: Data |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned char usb_buf[KEYCHRON_QHE_PACKET_SIZE + 1];
|
||||
|
||||
memset(usb_buf, 0, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Write command, offsetting by 1 for HID report ID |
|
||||
\*-----------------------------------------------------*/
|
||||
usb_buf[1] = cmd;
|
||||
memcpy(&usb_buf[2], data_in, data_in_size);
|
||||
|
||||
hid_write(dev, usb_buf, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Read response |
|
||||
\*-----------------------------------------------------*/
|
||||
int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf) - 1, 1000);
|
||||
|
||||
if(usb_buf[0] != cmd)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
|
||||
memcpy(data_out, &usb_buf[1], data_out_size);
|
||||
|
||||
return(bytes_received - 1);
|
||||
}
|
||||
|
||||
int QMKKeychronController::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
|
||||
)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Standard VIA command with sub-command |
|
||||
| |
|
||||
| Byte 0: Command |
|
||||
| Byte 1: Sub-Command |
|
||||
| Byte 2+: Data |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned char usb_buf[KEYCHRON_QHE_PACKET_SIZE + 1];
|
||||
|
||||
memset(usb_buf, 0, sizeof(usb_buf));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Write command, offsetting by 1 for HID report ID |
|
||||
\*-----------------------------------------------------*/
|
||||
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));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Read response |
|
||||
\*-----------------------------------------------------*/
|
||||
int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf) - 1, 1000);
|
||||
|
||||
if(usb_buf[0] != cmd || usb_buf[1] != subcmd)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
|
||||
memcpy(data_out, &usb_buf[2], data_out_size);
|
||||
|
||||
return(bytes_received - 2);
|
||||
}
|
||||
@@ -0,0 +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
|
||||
);
|
||||
};
|
||||
@@ -0,0 +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);
|
||||
@@ -0,0 +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();
|
||||
}
|
||||
@@ -0,0 +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;
|
||||
};
|
||||
@@ -0,0 +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,
|
||||
};
|
||||
@@ -186,7 +186,7 @@ unsigned short QMKVialRGBController::GetLEDCount()
|
||||
return(number_leds);
|
||||
}
|
||||
|
||||
vialrgb_led_info QMKVialRGBController::GetLEDInfo(unsigned short led_index)
|
||||
qmk_rgb_matrix_led_info QMKVialRGBController::GetLEDInfo(unsigned short led_index)
|
||||
{
|
||||
return(led_info[led_index]);
|
||||
}
|
||||
@@ -257,12 +257,12 @@ unsigned short QMKVialRGBController::CmdGetKeycode
|
||||
return(keycode);
|
||||
}
|
||||
|
||||
vialrgb_led_info QMKVialRGBController::CmdGetLEDInfo
|
||||
qmk_rgb_matrix_led_info QMKVialRGBController::CmdGetLEDInfo
|
||||
(
|
||||
unsigned short led_index
|
||||
)
|
||||
{
|
||||
vialrgb_led_info data;
|
||||
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));
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <hidapi.h>
|
||||
#include "ResourceManager.h"
|
||||
#include "QMKCommon.h"
|
||||
#include "RGBController.h"
|
||||
|
||||
#define MSG_LEN 32
|
||||
@@ -98,34 +99,25 @@ enum
|
||||
VIALRGB_EFFECT_SKIP = 0xFFFF
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char x;
|
||||
unsigned char y;
|
||||
unsigned char flags;
|
||||
unsigned char row;
|
||||
unsigned char col;
|
||||
} vialrgb_led_info;
|
||||
|
||||
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();
|
||||
std::string GetLocation();
|
||||
std::string GetName();
|
||||
std::string GetSerial();
|
||||
std::string GetVendor();
|
||||
std::string GetVersion();
|
||||
|
||||
bool GetSupported();
|
||||
bool GetSupported();
|
||||
|
||||
unsigned short GetEffect(std::size_t effect_idx);
|
||||
std::size_t GetEffectCount();
|
||||
unsigned short GetKeycode(unsigned short led_index);
|
||||
unsigned short GetLEDCount();
|
||||
vialrgb_led_info GetLEDInfo(unsigned short led_index);
|
||||
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
|
||||
(
|
||||
@@ -152,22 +144,22 @@ public:
|
||||
);
|
||||
|
||||
private:
|
||||
hid_device* dev;
|
||||
unsigned long long keyboard_uid;
|
||||
std::vector<unsigned short> keycodes;
|
||||
std::vector<vialrgb_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;
|
||||
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
|
||||
(
|
||||
@@ -176,7 +168,7 @@ private:
|
||||
unsigned char col
|
||||
);
|
||||
|
||||
vialrgb_led_info CmdGetLEDInfo
|
||||
qmk_rgb_matrix_led_info CmdGetLEDInfo
|
||||
(
|
||||
unsigned short led_index
|
||||
);
|
||||
|
||||
@@ -171,7 +171,7 @@ void RGBController_QMKVialRGB::SetupZones()
|
||||
|
||||
for(unsigned short led_index = 0; led_index < controller->GetLEDCount(); led_index++)
|
||||
{
|
||||
vialrgb_led_info info = controller->GetLEDInfo(led_index);
|
||||
qmk_rgb_matrix_led_info info = controller->GetLEDInfo(led_index);
|
||||
|
||||
if(info.col > max_col)
|
||||
{
|
||||
@@ -193,7 +193,7 @@ void RGBController_QMKVialRGB::SetupZones()
|
||||
|
||||
for(unsigned short led_index = 0; led_index < controller->GetLEDCount(); led_index++)
|
||||
{
|
||||
vialrgb_led_info info = controller->GetLEDInfo(led_index);
|
||||
qmk_rgb_matrix_led_info info = controller->GetLEDInfo(led_index);
|
||||
|
||||
matrix_map[(width * info.row) + info.col] = (unsigned int)led_index;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,8 @@ void RGBController_Razer::SetupZones()
|
||||
\*---------------------------------------------------------*/
|
||||
if(new_zone.type == ZONE_TYPE_MATRIX)
|
||||
{
|
||||
if(new_zone.name == ZONE_EN_KEYBOARD && device_list[device_index]->layout != NULL)
|
||||
if(device_list[device_index]->layout != NULL &&
|
||||
(new_zone.name == ZONE_EN_KEYBOARD || new_zone.name == "Keypad"))
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Dynamically generate a keyboard layout |
|
||||
@@ -212,7 +213,8 @@ void RGBController_Razer::SetupZones()
|
||||
new_map->width = device_list[device_index]->zones[zone_id]->cols;
|
||||
new_map->map = new unsigned int[new_map->height * new_map->width];
|
||||
|
||||
if(device_list[device_index]->layout->base_size != KEYBOARD_SIZE::KEYBOARD_SIZE_EMPTY)
|
||||
if(device_list[device_index]->layout->base_size != KEYBOARD_SIZE::KEYBOARD_SIZE_EMPTY ||
|
||||
!device_list[device_index]->layout->edit_keys.empty())
|
||||
{
|
||||
/*---------------------------------------------------------*\
|
||||
| Minor adjustments to keyboard layout |
|
||||
|
||||
@@ -443,6 +443,8 @@ bool RazerController::SupportsWave()
|
||||
case RAZER_BLACKWIDOW_V4_X_PID:
|
||||
case RAZER_BLACKWIDOW_V4_TKL_WIRED_PID:
|
||||
case RAZER_BLACKWIDOW_V4_TKL_WIRELESS_PID:
|
||||
case RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRED_PID:
|
||||
case RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRELESS_PID:
|
||||
case RAZER_BLACKWIDOW_X_CHROMA_PID:
|
||||
case RAZER_BLACKWIDOW_X_CHROMA_TE_PID:
|
||||
case RAZER_BLADE_2016_PID:
|
||||
|
||||
@@ -237,13 +237,16 @@ REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V3 TKL", Det
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V3 Mini (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V3_MINI_WIRED_PID, 0x03, 0x0C, 0x01);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V3 Mini (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V3_MINI_WIRELESS_PID, 0x03, 0x0C, 0x01);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PID, 0x03, 0x01, 0x00);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PRO_PID, 0x03, 0x01, 0x00);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro", /* firmware < 1.5 */ DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PRO_PID, 0x03, 0x01, 0x00);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro", /* firmware >= 1.5 */ DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PRO_PID, 0x03, 0x0C, 0x01);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro 75% (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PRO_75_WIRED_PID, 0x03, 0x0C, 0x01);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro 75% (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PRO_75_WIRELESS_PID, 0x02, 0x01, 0x02);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 75% (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_75_WIRED_PID, 0x03, 0x01, 0x00);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 X", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_X_PID, 0x02, 0x01, 0x02);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 TKL (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_TKL_WIRED_PID, 0x03, 0x0C, 0x01);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 TKL (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_TKL_WIRELESS_PID, 0x02, 0x01, 0x02);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Low Profile TKL (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRED_PID, 0x03, 0x0C, 0x01);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Low Profile TKL (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRELESS_PID, 0x02, 0x01, 0x02);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow X Chroma", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_X_CHROMA_PID, 0x02, 0x01, 0x02);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow X Chroma Tournament Edition", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_X_CHROMA_TE_PID, 0x02, 0x01, 0x02);
|
||||
REGISTER_HID_DETECTOR_IPU("Razer Cynosa Chroma", DetectRazerControllers, RAZER_VID, RAZER_CYNOSA_CHROMA_PID, 0x02, 0x01, 0x02);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user