Skip to content

Commit 8a63cfb

Browse files
Eclipse-SolCalcProgrammer1
authored andcommitted
Add Cooler Master GD160 ARGB Gaming Desk support
1 parent d4e802f commit 8a63cfb

5 files changed

Lines changed: 582 additions & 0 deletions

File tree

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*---------------------------------------------------------*\
2+
| CMGD160Controller.cpp |
3+
| |
4+
| Driver for Cooler Master GD160 ARGB Gaming Desk |
5+
| |
6+
| Logan Phillips (Eclipse) 16 Oct 2025 |
7+
| |
8+
| This file is part of the OpenRGB project |
9+
| Adapted from CMMonitor controller code |
10+
| SPDX-License-Identifier: GPL-2.0-or-later |
11+
\*---------------------------------------------------------*/
12+
13+
#include <cstring>
14+
#include "CMGD160Controller.h"
15+
#include "StringUtils.h"
16+
17+
CMGD160Controller::CMGD160Controller(hid_device* dev_handle, const hid_device_info& info, const std::string& name)
18+
{
19+
dev = dev_handle;
20+
device_name = name;
21+
location = info.path;
22+
ResetDevice();
23+
}
24+
25+
CMGD160Controller::~CMGD160Controller()
26+
{
27+
hid_close(dev);
28+
}
29+
30+
std::string CMGD160Controller::GetDeviceName()
31+
{
32+
return(device_name);
33+
}
34+
35+
std::string CMGD160Controller::GetDeviceLocation()
36+
{
37+
return("HID: " + location);
38+
}
39+
40+
std::string CMGD160Controller::GetSerialString()
41+
{
42+
wchar_t serial_string[128];
43+
int ret = hid_get_serial_number_string(dev, serial_string, 128);
44+
45+
if(ret != 0)
46+
{
47+
return("");
48+
}
49+
50+
return(StringUtils::wstring_to_string(serial_string));
51+
}
52+
53+
/*------------------------------------------------------------*\
54+
| Desk requires 2 sets of packets sent for the front and back |
55+
| Technically you could have both sides do something different |
56+
| Not sure why you would though.... |
57+
| Cooler Master's software doesn't allow that anyways |
58+
\*------------------------------------------------------------*/
59+
60+
void CMGD160Controller::SetMode(uint8_t mode_value, uint8_t speed, uint8_t brightness, const RGBColor& color)
61+
{
62+
if(is_software_mode_enabled)
63+
{
64+
SetControlMode(false);
65+
}
66+
67+
uint8_t usb_buf[CM_GD160_PACKET_LENGTH];
68+
69+
for(int side = 1; side <= 2; side++)
70+
{
71+
memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH);
72+
73+
usb_buf[1] = 0x80;
74+
usb_buf[2] = (mode_value == CM_GD160_OFF_MODE) ? 0x0F : 0x0B;
75+
usb_buf[3] = 0x02;
76+
usb_buf[4] = side; // 0x01 for front, 0x02 for back
77+
usb_buf[5] = mode_value;
78+
usb_buf[6] = (mode_value == CM_GD160_OFF_MODE) ? 0x00 : 0x08;
79+
usb_buf[7] = speed;
80+
usb_buf[8] = brightness;
81+
usb_buf[9] = RGBGetRValue(color);
82+
usb_buf[10] = RGBGetGValue(color);
83+
usb_buf[11] = RGBGetBValue(color);
84+
85+
hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH);
86+
}
87+
}
88+
89+
/*-------------------------------------------------*\
90+
| How to request current color in custom mode. |
91+
| Not like it matters since we default to direct |
92+
| mode and it seems to clear the current colors... |
93+
| |
94+
| memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH); |
95+
| usb_buf[1] = 0x80; |
96+
| usb_buf[2] = 0x10; |
97+
| usb_buf[3] = 0x01; or 0x02 |
98+
| usb_buf[4] = 0x02; |
99+
| usb_buf[5] = 0x80; |
100+
| hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH); |
101+
\*-------------------------------------------------*/
102+
103+
void CMGD160Controller::SendColorData(const std::vector<RGBColor>& colors, uint8_t command, uint8_t mode_byte, uint8_t brightness, bool desired_control_mode)
104+
{
105+
if(is_software_mode_enabled != desired_control_mode)
106+
{
107+
SetControlMode(desired_control_mode);
108+
}
109+
110+
uint8_t color_data[CM_GD160_COLOR_DATA_LENGTH];
111+
memset(color_data, 0x00, CM_GD160_COLOR_DATA_LENGTH);
112+
113+
for(unsigned int i = 0; i < colors.size() && i < (CM_GD160_LEDS_PER_SIDE * 2); i++)
114+
{
115+
unsigned int side = i / CM_GD160_LEDS_PER_SIDE;
116+
unsigned int led_in_side = i % CM_GD160_LEDS_PER_SIDE;
117+
unsigned int buffer_offset = (side * CM_GD160_SIDE_DATA_LENGTH) + (led_in_side * 3);
118+
119+
color_data[buffer_offset] = RGBGetRValue(colors[i]);
120+
color_data[buffer_offset + 1] = RGBGetGValue(colors[i]);
121+
color_data[buffer_offset + 2] = RGBGetBValue(colors[i]);
122+
}
123+
124+
uint8_t usb_buf[CM_GD160_PACKET_LENGTH];
125+
126+
for(unsigned int side = 1; side <= 2; side++)
127+
{
128+
unsigned int offset = (side - 1) * CM_GD160_SIDE_DATA_LENGTH;
129+
130+
for(unsigned int packet = 0; packet < 7; packet++)
131+
{
132+
memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH);
133+
134+
usb_buf[1] = (packet < 6) ? packet : 0x86; // Last packet uses 0x86
135+
136+
/*---------------------------------------------------------*\
137+
| First packet contains static data |
138+
\*---------------------------------------------------------*/
139+
140+
if(packet == 0)
141+
{
142+
usb_buf[2] = command;
143+
usb_buf[3] = 0x02;
144+
usb_buf[4] = side; // 0x01 for front, 0x02 for back
145+
usb_buf[5] = mode_byte;
146+
usb_buf[6] = brightness;
147+
148+
memcpy(&usb_buf[7], &color_data[offset], CM_GD160_FIRST_PACKET_DATA_SIZE);
149+
offset += CM_GD160_FIRST_PACKET_DATA_SIZE;
150+
}
151+
else
152+
{
153+
memcpy(&usb_buf[2], &color_data[offset], CM_GD160_PACKET_DATA_SIZE);
154+
offset += CM_GD160_PACKET_DATA_SIZE;
155+
}
156+
157+
hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH);
158+
}
159+
}
160+
}
161+
162+
/*------------------------------------------------------*\
163+
| True enables software mode |
164+
| False enables hardware mode |
165+
\*------------------------------------------------------*/
166+
167+
void CMGD160Controller::SetControlMode(bool software_mode)
168+
{
169+
uint8_t usb_buf[CM_GD160_PACKET_LENGTH];
170+
171+
for(int side = 1; side <= 2; side++)
172+
{
173+
memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH);
174+
175+
usb_buf[1] = 0x80;
176+
usb_buf[2] = 0x07;
177+
usb_buf[3] = 0x02;
178+
usb_buf[4] = side; // 0x01 for front, 0x02 for back
179+
usb_buf[6] = software_mode;
180+
181+
hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH);
182+
}
183+
184+
is_software_mode_enabled = software_mode;
185+
}
186+
187+
/*------------------------------------------------------*\
188+
| Reset device on discovery in case it somehow landed |
189+
| in a bad / unresponsive state |
190+
\*------------------------------------------------------*/
191+
192+
void CMGD160Controller::ResetDevice()
193+
{
194+
uint8_t usb_buf[CM_GD160_PACKET_LENGTH];
195+
196+
memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH);
197+
usb_buf[1] = 0x80;
198+
usb_buf[2] = 0x11;
199+
hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH);
200+
201+
memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH);
202+
usb_buf[1] = 0x80;
203+
usb_buf[2] = 0x0B;
204+
usb_buf[3] = 0x01;
205+
usb_buf[4] = 0x02;
206+
hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH);
207+
208+
memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH);
209+
usb_buf[1] = 0x80;
210+
usb_buf[2] = 0x18;
211+
usb_buf[3] = 0x01;
212+
usb_buf[4] = 0x02;
213+
hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH);
214+
215+
is_software_mode_enabled = false;
216+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*---------------------------------------------------------*\
2+
| CMGD160Controller.h |
3+
| |
4+
| Driver for Cooler Master GD160 ARGB Gaming Desk |
5+
| |
6+
| Logan Phillips (Eclipse) 16 Oct 2025 |
7+
| |
8+
| This file is part of the OpenRGB project |
9+
| Adapted from CMMonitor controller code |
10+
| SPDX-License-Identifier: GPL-2.0-or-later |
11+
\*---------------------------------------------------------*/
12+
13+
#pragma once
14+
15+
#include <string>
16+
#include <hidapi.h>
17+
#include "RGBController.h"
18+
19+
#define CM_GD160_PACKET_LENGTH 65
20+
#define CM_GD160_COLOR_DATA_LENGTH 872
21+
#define CM_GD160_SIDE_DATA_LENGTH 436 // 96 LEDs * 3 bytes + header = 436 bytes per side
22+
#define CM_GD160_LEDS_PER_SIDE 96
23+
#define CM_GD160_FIRST_PACKET_DATA_SIZE 58 // CM_GD160_PACKET_LENGTH - 7 (header bytes)
24+
#define CM_GD160_PACKET_DATA_SIZE 63 // CM_GD160_PACKET_LENGTH - 2 (header bytes)
25+
26+
enum
27+
{
28+
CM_GD160_DIRECT_MODE = 0xFF,
29+
CM_GD160_CUSTOM_MODE = 0xFE,
30+
CM_GD160_SPECTRUM_MODE = 0x00,
31+
CM_GD160_RELOAD_MODE = 0x01,
32+
CM_GD160_RECOIL_MODE = 0x02,
33+
CM_GD160_BREATHING_MODE = 0x03,
34+
CM_GD160_REFILL_MODE = 0x04,
35+
CM_GD160_OFF_MODE = 0x06
36+
};
37+
38+
enum
39+
{
40+
CM_GD160_BRIGHTNESS_MAX = 0xFF,
41+
CM_GD160_BRIGHTNESS_MIN = 0x00,
42+
CM_GD160_SPEED_MAX = 0x04,
43+
CM_GD160_SPEED_MIN = 0x00,
44+
};
45+
46+
class CMGD160Controller
47+
{
48+
public:
49+
CMGD160Controller(hid_device* dev_handle, const hid_device_info& info, const std::string& name);
50+
~CMGD160Controller();
51+
52+
std::string GetDeviceName();
53+
std::string GetSerialString();
54+
std::string GetDeviceLocation();
55+
56+
void SetMode(uint8_t mode_value, uint8_t speed, uint8_t brightness, const RGBColor& color);
57+
void SendColorData(const std::vector<RGBColor>& colors, uint8_t command, uint8_t mode_byte, uint8_t brightness, bool enable_software_mode);
58+
59+
private:
60+
std::string device_name;
61+
std::string serial_number;
62+
std::string location;
63+
hid_device* dev;
64+
bool is_software_mode_enabled = false;
65+
void SetControlMode(bool value);
66+
void ResetDevice();
67+
};

0 commit comments

Comments
 (0)