Skip to content

Commit 946383b

Browse files
mikeeqCalcProgrammer1
authored andcommitted
Add Fnatic STREAK and miniSTREAK support
1 parent 7ef7edb commit 946383b

5 files changed

Lines changed: 1277 additions & 0 deletions

File tree

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
/*---------------------------------------------------------*\
2+
| FnaticStreakController.cpp |
3+
| |
4+
| Driver for Fnatic Streak and miniStreak keyboards |
5+
| |
6+
| Based on leddy project by Hanna Czenczek |
7+
| |
8+
| This file is part of the OpenRGB project |
9+
| SPDX-License-Identifier: GPL-2.0-or-later |
10+
\*---------------------------------------------------------*/
11+
12+
#include <cstring>
13+
#include "FnaticStreakController.h"
14+
#include "StringUtils.h"
15+
#include "LogManager.h"
16+
17+
FnaticStreakController::FnaticStreakController(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name, FnaticStreakType kb_type)
18+
{
19+
dev = dev_handle;
20+
location = dev_info->path;
21+
name = dev_name;
22+
keyboard_type = kb_type;
23+
profile = 1;
24+
software_effect_mode = false;
25+
26+
memset(color_buf, 0x00, sizeof(color_buf));
27+
28+
/*-----------------------------------------------------*\
29+
| Get the firmware version from the device info |
30+
\*-----------------------------------------------------*/
31+
char fw_version_buf[8];
32+
memset(fw_version_buf, '\0', sizeof(fw_version_buf));
33+
34+
unsigned short version = dev_info->release_number;
35+
snprintf(fw_version_buf, 8, "%.2X.%.2X", (version & 0xFF00) >> 8, version & 0x00FF);
36+
37+
firmware_version = fw_version_buf;
38+
}
39+
40+
FnaticStreakController::~FnaticStreakController()
41+
{
42+
hid_close(dev);
43+
}
44+
45+
std::string FnaticStreakController::GetDeviceLocation()
46+
{
47+
return("HID " + location);
48+
}
49+
50+
std::string FnaticStreakController::GetNameString()
51+
{
52+
return(name);
53+
}
54+
55+
std::string FnaticStreakController::GetSerialString()
56+
{
57+
wchar_t serial_string[128];
58+
int ret = hid_get_serial_number_string(dev, serial_string, 128);
59+
60+
if(ret != 0)
61+
{
62+
return("");
63+
}
64+
65+
return(StringUtils::wstring_to_string(serial_string));
66+
}
67+
68+
std::string FnaticStreakController::GetFirmwareVersion()
69+
{
70+
return(firmware_version);
71+
}
72+
73+
FnaticStreakType FnaticStreakController::GetKeyboardType()
74+
{
75+
return(keyboard_type);
76+
}
77+
78+
unsigned int FnaticStreakController::GetLEDCount()
79+
{
80+
if(keyboard_type == FNATIC_STREAK_TYPE_MINI)
81+
{
82+
return 106;
83+
}
84+
else
85+
{
86+
return 124;
87+
}
88+
}
89+
90+
void FnaticStreakController::SetProfile(unsigned char new_profile)
91+
{
92+
if(new_profile >= 1 && new_profile <= 4)
93+
{
94+
profile = new_profile;
95+
SoftwareEffectEnd();
96+
}
97+
}
98+
99+
void FnaticStreakController::SoftwareEffectStart()
100+
{
101+
software_effect_mode = true;
102+
}
103+
104+
void FnaticStreakController::SoftwareEffectEnd()
105+
{
106+
software_effect_mode = false;
107+
RefreshProfile();
108+
}
109+
110+
void FnaticStreakController::SendKeepalive()
111+
{
112+
/*-----------------------------------------------------*\
113+
| Send keepalive packet (0x07 or 0xfe) to prevent |
114+
| keyboard from reverting to profile effect during |
115+
| direct/preview mode |
116+
\*-----------------------------------------------------*/
117+
unsigned char prefix[] = { 0x07 };
118+
SendRequest(prefix, sizeof(prefix), nullptr, 0);
119+
}
120+
121+
void FnaticStreakController::RefreshProfile()
122+
{
123+
unsigned char data[] = { profile };
124+
unsigned char prefix[] = { 0x04 };
125+
SendRequest(prefix, sizeof(prefix), data, sizeof(data));
126+
}
127+
128+
void FnaticStreakController::SendRequest(const unsigned char* prefix, size_t prefix_len, const unsigned char* raw_data, size_t data_len)
129+
{
130+
size_t total_len = prefix_len + data_len;
131+
size_t offset = 0;
132+
unsigned char cmd = (prefix_len > 0) ? prefix[0] : raw_data[0];
133+
134+
while(offset < total_len)
135+
{
136+
unsigned char packet[65];
137+
memset(packet, 0x00, sizeof(packet));
138+
139+
/*-----------------------------------------------------*\
140+
| Packet format: |
141+
| [0] = Report ID (0x00) |
142+
| [1] = Command |
143+
| [2-4] = Total length (24-bit little endian) |
144+
| [5-7] = Offset (24-bit little endian) |
145+
| [8-64] = Data (57 bytes max per packet) |
146+
\*-----------------------------------------------------*/
147+
packet[0] = 0x00;
148+
packet[1] = cmd;
149+
packet[2] = (unsigned char)(total_len & 0xFF);
150+
packet[3] = (unsigned char)((total_len >> 8) & 0xFF);
151+
packet[4] = (unsigned char)((total_len >> 16) & 0xFF);
152+
packet[5] = (unsigned char)(offset & 0xFF);
153+
packet[6] = (unsigned char)((offset >> 8) & 0xFF);
154+
packet[7] = (unsigned char)((offset >> 16) & 0xFF);
155+
156+
for(size_t i = offset; i < offset + 57 && i < total_len; i++)
157+
{
158+
if(i < prefix_len)
159+
{
160+
packet[i - offset + 8] = prefix[i];
161+
}
162+
else
163+
{
164+
packet[i - offset + 8] = raw_data[i - prefix_len];
165+
}
166+
}
167+
168+
hid_write(dev, packet, 65);
169+
offset += 57;
170+
}
171+
172+
/*-----------------------------------------------------*\
173+
| For command 0x05, save changes and refresh profile |
174+
\*-----------------------------------------------------*/
175+
if(cmd == 0x05)
176+
{
177+
unsigned char save_prefix[] = { 0x13 };
178+
SendRequest(save_prefix, sizeof(save_prefix), nullptr, 0);
179+
180+
unsigned char profile_data[] = { profile };
181+
unsigned char profile_prefix[] = { 0x04 };
182+
SendRequest(profile_prefix, sizeof(profile_prefix), profile_data, sizeof(profile_data));
183+
}
184+
}
185+
186+
void FnaticStreakController::SetLEDsDirect(std::vector<led> leds, std::vector<RGBColor> colors, unsigned int brightness)
187+
{
188+
unsigned int total_leds = GetLEDCount();
189+
190+
/*-----------------------------------------------------*\
191+
| Clear the color buffer |
192+
\*-----------------------------------------------------*/
193+
memset(color_buf, 0x00, sizeof(color_buf));
194+
195+
/*-----------------------------------------------------*\
196+
| Transfer colors to the buffer |
197+
| Format: sequential RGB triplets indexed by LED value |
198+
| The LED value corresponds to the physical LED index |
199+
| in the keyboard hardware (0-123 for full, 0-105 mini) |
200+
| Apply brightness scaling (0-100%) |
201+
\*-----------------------------------------------------*/
202+
unsigned int leds_to_set = (unsigned int)std::min(colors.size(), leds.size());
203+
204+
for(unsigned int i = 0; i < leds_to_set; i++)
205+
{
206+
unsigned int led_idx = leds[i].value;
207+
if(led_idx < total_leds)
208+
{
209+
if(brightness >= 100)
210+
{
211+
/*-----------------------------------------*\
212+
| Full brightness - no scaling needed |
213+
\*-----------------------------------------*/
214+
color_buf[led_idx * 3 + 0] = RGBGetRValue(colors[i]);
215+
color_buf[led_idx * 3 + 1] = RGBGetGValue(colors[i]);
216+
color_buf[led_idx * 3 + 2] = RGBGetBValue(colors[i]);
217+
}
218+
else
219+
{
220+
/*-----------------------------------------*\
221+
| Apply brightness scaling |
222+
\*-----------------------------------------*/
223+
color_buf[led_idx * 3 + 0] = (unsigned char)(RGBGetRValue(colors[i]) * brightness / 100);
224+
color_buf[led_idx * 3 + 1] = (unsigned char)(RGBGetGValue(colors[i]) * brightness / 100);
225+
color_buf[led_idx * 3 + 2] = (unsigned char)(RGBGetBValue(colors[i]) * brightness / 100);
226+
}
227+
}
228+
}
229+
}
230+
231+
void FnaticStreakController::SendRGBToDevice()
232+
{
233+
unsigned int total_leds = GetLEDCount();
234+
unsigned int data_size = total_leds * 3;
235+
236+
/*-----------------------------------------------------*\
237+
| For direct/software control, use command 0x0f |
238+
| This bypasses the profile and allows immediate update |
239+
| The 0x03 subcommand indicates per-key color data |
240+
\*-----------------------------------------------------*/
241+
unsigned char prefix[] = { 0x0f, 0x03 };
242+
SendRequest(prefix, sizeof(prefix), color_buf, data_size);
243+
}
244+
245+
void FnaticStreakController::SetPulse(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed)
246+
{
247+
/*-----------------------------------------------------*\
248+
| Pulse effect - cmd 0x06 |
249+
| Data: [mode, r, g, b, speed] |
250+
\*-----------------------------------------------------*/
251+
unsigned char prefix[] = { 0x05, profile, 0x02 };
252+
unsigned char data[] = { FNATIC_STREAK_CMD_PULSE, color_mode, r, g, b, speed };
253+
SendRequest(prefix, sizeof(prefix), data, sizeof(data));
254+
}
255+
256+
void FnaticStreakController::SetWave(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, unsigned char direction)
257+
{
258+
/*-----------------------------------------------------*\
259+
| Wave effect - cmd 0x07 |
260+
| Data: [mode, r, g, b, speed, direction] |
261+
\*-----------------------------------------------------*/
262+
unsigned char prefix[] = { 0x05, profile, 0x02 };
263+
unsigned char data[] = { FNATIC_STREAK_CMD_WAVE, color_mode, r, g, b, speed, direction };
264+
SendRequest(prefix, sizeof(prefix), data, sizeof(data));
265+
}
266+
267+
void FnaticStreakController::SetReactive(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, bool keyup)
268+
{
269+
/*-----------------------------------------------------*\
270+
| Reactive effect - cmd 0x09 |
271+
| Data: [mode, r, g, b, speed, trigger] |
272+
| trigger: 0 = keydown, 1 = keyup |
273+
\*-----------------------------------------------------*/
274+
unsigned char prefix[] = { 0x05, profile, 0x02 };
275+
unsigned char data[] = { FNATIC_STREAK_CMD_REACTIVE, color_mode, r, g, b, speed, (unsigned char)(keyup ? 0 : 1) };
276+
SendRequest(prefix, sizeof(prefix), data, sizeof(data));
277+
}
278+
279+
void FnaticStreakController::SetReactiveRipple(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, bool keyup)
280+
{
281+
/*-----------------------------------------------------*\
282+
| Reactive Ripple effect - cmd 0x0A |
283+
| Data: [mode, r, g, b, speed, trigger] |
284+
| trigger: 0 = keydown, 1 = keyup |
285+
\*-----------------------------------------------------*/
286+
unsigned char prefix[] = { 0x05, profile, 0x02 };
287+
unsigned char data[] = { FNATIC_STREAK_CMD_REACTIVE_RIPPLE, color_mode, r, g, b, speed, (unsigned char)(keyup ? 0 : 1) };
288+
SendRequest(prefix, sizeof(prefix), data, sizeof(data));
289+
}
290+
291+
void FnaticStreakController::SetRain(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, unsigned char direction)
292+
{
293+
/*-----------------------------------------------------*\
294+
| Rain effect - cmd 0x0B |
295+
| Data: [mode, r, g, b, speed, direction] |
296+
| Note: Does not support rainbow mode |
297+
\*-----------------------------------------------------*/
298+
unsigned char prefix[] = { 0x05, profile, 0x02 };
299+
unsigned char mode = (color_mode == FNATIC_STREAK_COLOR_MODE_RAINBOW) ? FNATIC_STREAK_COLOR_MODE_RANDOM : color_mode;
300+
unsigned char data[] = { FNATIC_STREAK_CMD_RAIN, mode, r, g, b, speed, direction };
301+
SendRequest(prefix, sizeof(prefix), data, sizeof(data));
302+
}
303+
304+
void FnaticStreakController::SetGradient(unsigned char colors[][3], unsigned char positions[], unsigned int count)
305+
{
306+
/*-----------------------------------------------------*\
307+
| Gradient effect - cmd 0x0C |
308+
| Data: [count, {r, g, b, pos} * 10] |
309+
| (always sends 10 color slots, unused are zeroed) |
310+
\*-----------------------------------------------------*/
311+
unsigned char prefix[] = { 0x05, profile, 0x02 };
312+
unsigned char data[42];
313+
memset(data, 0x00, sizeof(data));
314+
315+
data[0] = FNATIC_STREAK_CMD_GRADIENT;
316+
data[1] = (unsigned char)count;
317+
318+
for(unsigned int i = 0; i < count && i < 10; i++)
319+
{
320+
data[2 + i * 4 + 0] = colors[i][0];
321+
data[2 + i * 4 + 1] = colors[i][1];
322+
data[2 + i * 4 + 2] = colors[i][2];
323+
data[2 + i * 4 + 3] = positions[i];
324+
}
325+
326+
SendRequest(prefix, sizeof(prefix), data, sizeof(data));
327+
}
328+
329+
void FnaticStreakController::SetFade(unsigned char color_mode, unsigned char colors[][3], unsigned char positions[], unsigned int count, unsigned char speed)
330+
{
331+
/*-----------------------------------------------------*\
332+
| Fade effect - cmd 0x0D |
333+
| Data: [mode, count, {r, g, b, pos} * 10, speed] |
334+
\*-----------------------------------------------------*/
335+
unsigned char prefix[] = { 0x05, profile, 0x02 };
336+
unsigned char data[44];
337+
memset(data, 0x00, sizeof(data));
338+
339+
data[0] = FNATIC_STREAK_CMD_FADE;
340+
data[1] = color_mode;
341+
data[2] = (unsigned char)count;
342+
343+
for(unsigned int i = 0; i < count && i < 10; i++)
344+
{
345+
data[3 + i * 4 + 0] = colors[i][0];
346+
data[3 + i * 4 + 1] = colors[i][1];
347+
data[3 + i * 4 + 2] = colors[i][2];
348+
data[3 + i * 4 + 3] = positions[i];
349+
}
350+
351+
data[43] = speed;
352+
353+
SendRequest(prefix, sizeof(prefix), data, sizeof(data));
354+
}

0 commit comments

Comments
 (0)