1use crate::api_commands::{
2 ViaChannelId, ViaCommandId, ViaQmkAudioValue, ViaQmkBacklightValue, ViaQmkLedMatrixValue,
3 ViaQmkRgbMatrixValue, ViaQmkRgblightValue,
4};
5use crate::utils;
6use hidapi::HidApi;
7use pyo3::prelude::*;
8use std::str::FromStr;
9use std::vec;
10
11const COMMAND_START: u8 = 0x00;
12
13pub const RAW_EPSIZE: usize = 32;
14pub const DATA_BUFFER_SIZE: usize = 28;
15
16pub const PROTOCOL_ALPHA: u16 = 7;
17pub const PROTOCOL_BETA: u16 = 8;
18pub const PROTOCOL_GAMMA: u16 = 9;
19
20pub type Layer = u8;
21pub type Row = u8;
22pub type Column = u8;
23
24#[pyclass]
25#[derive(Clone, Copy, Debug)]
26pub struct MatrixInfo {
27 pub rows: u8,
28 pub cols: u8,
29}
30
31#[pyclass]
32#[derive(Clone, Copy, Debug)]
33pub enum KeyboardValue {
34 Uptime = 0x01,
35 LayoutOptions = 0x02,
36 SwitchMatrixState = 0x03,
37 FirmwareVersion = 0x04,
38 DeviceIndication = 0x05,
39}
40
41impl FromStr for KeyboardValue {
42 type Err = &'static str;
43
44 fn from_str(s: &str) -> Result<Self, Self::Err> {
45 match s {
46 "Uptime" => Ok(KeyboardValue::Uptime),
47 "LayoutOptions" => Ok(KeyboardValue::LayoutOptions),
48 "SwitchMatrixState" => Ok(KeyboardValue::SwitchMatrixState),
49 "FirmwareVersion" => Ok(KeyboardValue::FirmwareVersion),
50 "DeviceIndication" => Ok(KeyboardValue::DeviceIndication),
51 _ => Err("Invalid KeyboardValue"),
52 }
53 }
54}
55
56#[pyclass]
57pub struct KeyboardApi {
58 device: hidapi::HidDevice,
59}
60
61#[pymethods]
62impl KeyboardApi {
63 #[new]
64 pub fn new(vid: u16, pid: u16, usage_page: u16) -> PyResult<Self> {
65 let api = HidApi::new().map_err(|e| {
66 PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(format!("Error: {}", e))
67 })?;
68
69 let device = api
70 .device_list()
71 .find(|device| {
72 device.vendor_id() == vid
73 && device.product_id() == pid
74 && device.usage_page() == usage_page
75 })
76 .ok_or_else(|| {
77 PyErr::new::<pyo3::exceptions::PyRuntimeError, _>("Could not find keyboard.")
78 })?
79 .open_device(&api)
80 .map_err(|_| {
81 PyErr::new::<pyo3::exceptions::PyRuntimeError, _>("Could not open HID device.")
82 })?;
83
84 Ok(KeyboardApi { device })
85 }
86
87 pub fn hid_command(&self, command: ViaCommandId, bytes: Vec<u8>) -> Option<Vec<u8>> {
89 let mut command_bytes: Vec<u8> = vec![command as u8];
90 command_bytes.extend(bytes);
91
92 self.hid_send(command_bytes.clone())?;
93
94 let buffer = self.hid_read()?;
95 if buffer.starts_with(&command_bytes) {
96 Some(buffer)
97 } else {
98 None
99 }
100 }
101
102 pub fn hid_read(&self) -> Option<Vec<u8>> {
104 let mut buffer = vec![0; RAW_EPSIZE];
105 match self.device.read(&mut buffer) {
106 Ok(_) => Some(buffer),
107 Err(_) => None,
108 }
109 }
110
111 pub fn hid_send(&self, bytes: Vec<u8>) -> Option<()> {
113 if bytes.len() > RAW_EPSIZE {
114 return None;
115 }
116
117 let mut command_bytes: Vec<u8> = vec![COMMAND_START];
118 command_bytes.extend(bytes);
119
120 let mut padded_array = vec![0; RAW_EPSIZE + 1];
121 for (idx, &val) in command_bytes.iter().enumerate() {
122 padded_array[idx] = val;
123 }
124
125 if self.device.write(&padded_array).ok()? == RAW_EPSIZE + 1 {
126 return Some(());
127 }
128
129 None
130 }
131
132 pub fn get_protocol_version(&self) -> Option<u16> {
134 self.hid_command(ViaCommandId::GetProtocolVersion, vec![])
135 .map(|val| utils::shift_to_16_bit(val[1], val[2]))
136 }
137
138 pub fn get_layer_count(&self) -> Option<u8> {
140 match self.get_protocol_version() {
141 Some(version) if version >= PROTOCOL_BETA => self
142 .hid_command(ViaCommandId::DynamicKeymapGetLayerCount, vec![])
143 .map(|val| val[1]),
144 Some(_) => Some(4),
145 _ => None,
146 }
147 }
148
149 pub fn get_key(&self, layer: Layer, row: Row, col: Column) -> Option<u16> {
151 self.hid_command(ViaCommandId::DynamicKeymapGetKeycode, vec![layer, row, col])
152 .map(|val| utils::shift_to_16_bit(val[4], val[5]))
153 }
154
155 pub fn set_key(&self, layer: Layer, row: Row, column: Column, val: u16) -> Option<u16> {
157 let val_bytes = utils::shift_from_16_bit(val);
158 let bytes = vec![layer, row, column, val_bytes.0, val_bytes.1];
159 self.hid_command(ViaCommandId::DynamicKeymapSetKeycode, bytes)
160 .map(|val| utils::shift_to_16_bit(val[4], val[5]))
161 }
162
163 pub fn read_raw_matrix(&self, matrix_info: MatrixInfo, layer: Layer) -> Option<Vec<u16>> {
165 match self.get_protocol_version() {
166 Some(version) if version >= PROTOCOL_BETA => {
167 self.fast_read_raw_matrix(matrix_info, layer)
168 }
169 Some(version) if version == PROTOCOL_ALPHA => {
170 self.slow_read_raw_matrix(matrix_info, layer)
171 }
172 _ => None,
173 }
174 }
175
176 fn get_keymap_buffer(&self, offset: u16, size: u8) -> Option<Vec<u8>> {
177 if size > DATA_BUFFER_SIZE as u8 {
178 return None;
179 }
180 let offset_bytes = utils::shift_from_16_bit(offset);
181 self.hid_command(
182 ViaCommandId::DynamicKeymapGetBuffer,
183 vec![offset_bytes.0, offset_bytes.1, size],
184 )
185 .map(|val| val[4..(size as usize + 4)].to_vec())
186 }
187
188 fn fast_read_raw_matrix(&self, matrix_info: MatrixInfo, layer: Layer) -> Option<Vec<u16>> {
189 const MAX_KEYCODES_PARTIAL: usize = 14;
190 let length = matrix_info.rows as usize * matrix_info.cols as usize;
191 let buffer_list = vec![0; length.div_ceil(MAX_KEYCODES_PARTIAL) as usize];
192 let mut remaining = length;
193 let mut result = Vec::new();
194 for _ in 0..buffer_list.len() {
195 if remaining < MAX_KEYCODES_PARTIAL {
196 self.get_keymap_buffer(
197 layer as u16 * length as u16 * 2 + 2 * (length - remaining) as u16,
198 (remaining * 2) as u8,
199 )
200 .map(|val| result.extend(val));
201 remaining = 0;
202 } else {
203 self.get_keymap_buffer(
204 layer as u16 * length as u16 * 2 + 2 * (length - remaining) as u16,
205 (MAX_KEYCODES_PARTIAL * 2) as u8,
206 )
207 .map(|val| result.extend(val));
208 remaining -= MAX_KEYCODES_PARTIAL;
209 }
210 }
211 Some(utils::shift_buffer_to_16_bit(&result))
212 }
213
214 fn slow_read_raw_matrix(&self, matrix_info: MatrixInfo, layer: Layer) -> Option<Vec<u16>> {
215 let length = matrix_info.rows as usize * matrix_info.cols as usize;
216 let mut res = Vec::new();
217 for i in 0..length {
218 let row = (i as u16 / matrix_info.cols as u16) as u8;
219 let col = (i as u16 % matrix_info.cols as u16) as u8;
220 self.get_key(layer, row, col).map(|val| res.push(val));
221 }
222 Some(res)
223 }
224
225 pub fn write_raw_matrix(&self, matrix_info: MatrixInfo, keymap: Vec<Vec<u16>>) -> Option<()> {
227 match self.get_protocol_version() {
228 Some(version) if version >= PROTOCOL_BETA => self.fast_write_raw_matrix(keymap),
229 Some(version) if version == PROTOCOL_ALPHA => {
230 self.slow_write_raw_matrix(matrix_info, keymap)
231 }
232 _ => None,
233 }
234 }
235
236 fn slow_write_raw_matrix(&self, matrix_info: MatrixInfo, keymap: Vec<Vec<u16>>) -> Option<()> {
237 for (layer_idx, layer) in keymap.iter().enumerate() {
238 for (key_idx, keycode) in layer.iter().enumerate() {
239 let row = (key_idx as u16 / matrix_info.cols as u16) as u8;
240 let col = (key_idx as u16 % matrix_info.cols as u16) as u8;
241 self.set_key(layer_idx as u8, row, col, *keycode)
242 .map(|_| ());
243 }
244 }
245 Some(())
246 }
247
248 fn fast_write_raw_matrix(&self, keymap: Vec<Vec<u16>>) -> Option<()> {
249 let data: Vec<u16> = keymap
250 .iter()
251 .flat_map(|layer| layer.iter().cloned())
252 .collect();
253 let shifted_data = utils::shift_buffer_from_16_bit(&data);
254 for offset in (0..shifted_data.len()).step_by(DATA_BUFFER_SIZE) {
255 let offset_bytes = utils::shift_from_16_bit(offset as u16);
256 let end = std::cmp::min(offset + DATA_BUFFER_SIZE, shifted_data.len());
257 let buffer = shifted_data[offset..end].to_vec();
258 let mut bytes = vec![offset_bytes.0, offset_bytes.1, buffer.len() as u8];
259 bytes.extend(buffer);
260 self.hid_command(ViaCommandId::DynamicKeymapSetBuffer, bytes)
261 .map(|_| ());
262 }
263 Some(())
264 }
265
266 pub fn get_keyboard_value(
268 &self,
269 command: KeyboardValue,
270 parameters: Vec<u8>,
271 result_length: usize,
272 ) -> Option<Vec<u8>> {
273 let parameters_length = parameters.len();
274 let mut bytes = vec![command as u8];
275 bytes.extend(parameters);
276 self.hid_command(ViaCommandId::GetKeyboardValue, bytes)
277 .map(|val| val[1 + parameters_length..1 + parameters_length + result_length].to_vec())
278 }
279
280 pub fn set_keyboard_value(&self, command: KeyboardValue, parameters: Vec<u8>) -> Option<()> {
282 let mut bytes = vec![command as u8];
283 bytes.extend(parameters);
284 self.hid_command(ViaCommandId::SetKeyboardValue, bytes)
285 .map(|_| ())
286 }
287
288 pub fn get_encoder_value(&self, layer: Layer, id: u8, is_clockwise: bool) -> Option<u16> {
290 self.hid_command(
291 ViaCommandId::DynamicKeymapGetEncoder,
292 vec![layer, id, is_clockwise as u8],
293 )
294 .map(|val| utils::shift_to_16_bit(val[4], val[5]))
295 }
296
297 pub fn set_encoder_value(
299 &self,
300 layer: Layer,
301 id: u8,
302 is_clockwise: bool,
303 keycode: u16,
304 ) -> Option<()> {
305 let keycode_bytes = utils::shift_from_16_bit(keycode);
306 let bytes = vec![
307 layer,
308 id,
309 is_clockwise as u8,
310 keycode_bytes.0,
311 keycode_bytes.1,
312 ];
313 self.hid_command(ViaCommandId::DynamicKeymapSetEncoder, bytes)
314 .map(|_| ())
315 }
316
317 pub fn get_custom_menu_value(&self, command_bytes: Vec<u8>) -> Option<Vec<u8>> {
319 let command_length = command_bytes.len();
320 self.hid_command(ViaCommandId::CustomMenuGetValue, command_bytes)
321 .map(|val| val[0..command_length].to_vec())
322 }
323
324 pub fn set_custom_menu_value(&self, args: Vec<u8>) -> Option<()> {
326 self.hid_command(ViaCommandId::CustomMenuSetValue, args)
327 .map(|_| ())
328 }
329
330 pub fn save_custom_menu(&self, channel: u8) -> Option<()> {
332 let bytes = vec![channel];
333 self.hid_command(ViaCommandId::CustomMenuSave, bytes)
334 .map(|_| ())
335 }
336
337 pub fn get_backlight_brightness(&self) -> Option<u8> {
339 self.hid_command(
340 ViaCommandId::CustomMenuGetValue,
341 vec![
342 ViaChannelId::IdQmkBacklightChannel as u8,
343 ViaQmkBacklightValue::IdQmkBacklightBrightness as u8,
344 ],
345 )
346 .map(|val| val[3])
347 }
348
349 pub fn set_backlight_brightness(&self, brightness: u8) -> Option<()> {
351 self.hid_command(
352 ViaCommandId::CustomMenuSetValue,
353 vec![
354 ViaChannelId::IdQmkBacklightChannel as u8,
355 ViaQmkBacklightValue::IdQmkBacklightBrightness as u8,
356 brightness,
357 ],
358 )
359 .map(|_| ())
360 }
361
362 pub fn get_backlight_effect(&self) -> Option<u8> {
364 self.hid_command(
365 ViaCommandId::CustomMenuGetValue,
366 vec![
367 ViaChannelId::IdQmkBacklightChannel as u8,
368 ViaQmkBacklightValue::IdQmkBacklightEffect as u8,
369 ],
370 )
371 .map(|val| val[3])
372 }
373
374 pub fn set_backlight_effect(&self, effect: u8) -> Option<()> {
376 self.hid_command(
377 ViaCommandId::CustomMenuSetValue,
378 vec![
379 ViaChannelId::IdQmkBacklightChannel as u8,
380 ViaQmkBacklightValue::IdQmkBacklightEffect as u8,
381 effect,
382 ],
383 )
384 .map(|_| ())
385 }
386
387 pub fn get_rgblight_brightness(&self) -> Option<u8> {
389 self.hid_command(
390 ViaCommandId::CustomMenuGetValue,
391 vec![
392 ViaChannelId::IdQmkRgblightChannel as u8,
393 ViaQmkRgblightValue::IdQmkRgblightBrightness as u8,
394 ],
395 )
396 .map(|val| val[3])
397 }
398
399 pub fn set_rgblight_brightness(&self, brightness: u8) -> Option<()> {
401 self.hid_command(
402 ViaCommandId::CustomMenuSetValue,
403 vec![
404 ViaChannelId::IdQmkRgblightChannel as u8,
405 ViaQmkRgblightValue::IdQmkRgblightBrightness as u8,
406 brightness,
407 ],
408 )
409 .map(|_| ())
410 }
411
412 pub fn get_rgblight_effect(&self) -> Option<u8> {
414 self.hid_command(
415 ViaCommandId::CustomMenuGetValue,
416 vec![
417 ViaChannelId::IdQmkRgblightChannel as u8,
418 ViaQmkRgblightValue::IdQmkRgblightEffect as u8,
419 ],
420 )
421 .map(|val| val[3])
422 }
423
424 pub fn set_rgblight_effect(&self, effect: u8) -> Option<()> {
426 self.hid_command(
427 ViaCommandId::CustomMenuSetValue,
428 vec![
429 ViaChannelId::IdQmkRgblightChannel as u8,
430 ViaQmkRgblightValue::IdQmkRgblightEffect as u8,
431 effect,
432 ],
433 )
434 .map(|_| ())
435 }
436
437 pub fn get_rgblight_effect_speed(&self) -> Option<u8> {
439 self.hid_command(
440 ViaCommandId::CustomMenuGetValue,
441 vec![
442 ViaChannelId::IdQmkRgblightChannel as u8,
443 ViaQmkRgblightValue::IdQmkRgblightEffectSpeed as u8,
444 ],
445 )
446 .map(|val| val[3])
447 }
448
449 pub fn set_rgblight_effect_speed(&self, speed: u8) -> Option<()> {
451 self.hid_command(
452 ViaCommandId::CustomMenuSetValue,
453 vec![
454 ViaChannelId::IdQmkRgblightChannel as u8,
455 ViaQmkRgblightValue::IdQmkRgblightEffectSpeed as u8,
456 speed,
457 ],
458 )
459 .map(|_| ())
460 }
461
462 pub fn get_rgblight_color(&self) -> Option<(u8, u8)> {
464 self.hid_command(
465 ViaCommandId::CustomMenuGetValue,
466 vec![
467 ViaChannelId::IdQmkRgblightChannel as u8,
468 ViaQmkRgblightValue::IdQmkRgblightColor as u8,
469 ],
470 )
471 .map(|val| (val[3], val[4]))
472 }
473
474 pub fn set_rgblight_color(&self, hue: u8, sat: u8) -> Option<()> {
476 self.hid_command(
477 ViaCommandId::CustomMenuSetValue,
478 vec![
479 ViaChannelId::IdQmkRgblightChannel as u8,
480 ViaQmkRgblightValue::IdQmkRgblightColor as u8,
481 hue,
482 sat,
483 ],
484 )
485 .map(|_| ())
486 }
487
488 pub fn get_rgb_matrix_brightness(&self) -> Option<u8> {
490 self.hid_command(
491 ViaCommandId::CustomMenuGetValue,
492 vec![
493 ViaChannelId::IdQmkRgbMatrixChannel as u8,
494 ViaQmkRgbMatrixValue::IdQmkRgbMatrixBrightness as u8,
495 ],
496 )
497 .map(|val| val[3])
498 }
499
500 pub fn set_rgb_matrix_brightness(&self, brightness: u8) -> Option<()> {
502 self.hid_command(
503 ViaCommandId::CustomMenuSetValue,
504 vec![
505 ViaChannelId::IdQmkRgbMatrixChannel as u8,
506 ViaQmkRgbMatrixValue::IdQmkRgbMatrixBrightness as u8,
507 brightness,
508 ],
509 )
510 .map(|_| ())
511 }
512
513 pub fn get_rgb_matrix_effect(&self) -> Option<u8> {
515 self.hid_command(
516 ViaCommandId::CustomMenuGetValue,
517 vec![
518 ViaChannelId::IdQmkRgbMatrixChannel as u8,
519 ViaQmkRgbMatrixValue::IdQmkRgbMatrixEffect as u8,
520 ],
521 )
522 .map(|val| val[3])
523 }
524
525 pub fn set_rgb_matrix_effect(&self, effect: u8) -> Option<()> {
527 self.hid_command(
528 ViaCommandId::CustomMenuSetValue,
529 vec![
530 ViaChannelId::IdQmkRgbMatrixChannel as u8,
531 ViaQmkRgbMatrixValue::IdQmkRgbMatrixEffect as u8,
532 effect,
533 ],
534 )
535 .map(|_| ())
536 }
537
538 pub fn get_rgb_matrix_effect_speed(&self) -> Option<u8> {
540 self.hid_command(
541 ViaCommandId::CustomMenuGetValue,
542 vec![
543 ViaChannelId::IdQmkRgbMatrixChannel as u8,
544 ViaQmkRgbMatrixValue::IdQmkRgbMatrixEffectSpeed as u8,
545 ],
546 )
547 .map(|val| val[3])
548 }
549
550 pub fn set_rgb_matrix_effect_speed(&self, speed: u8) -> Option<()> {
552 self.hid_command(
553 ViaCommandId::CustomMenuSetValue,
554 vec![
555 ViaChannelId::IdQmkRgbMatrixChannel as u8,
556 ViaQmkRgbMatrixValue::IdQmkRgbMatrixEffectSpeed as u8,
557 speed,
558 ],
559 )
560 .map(|_| ())
561 }
562
563 pub fn get_rgb_matrix_color(&self) -> Option<(u8, u8)> {
565 self.hid_command(
566 ViaCommandId::CustomMenuGetValue,
567 vec![
568 ViaChannelId::IdQmkRgbMatrixChannel as u8,
569 ViaQmkRgbMatrixValue::IdQmkRgbMatrixColor as u8,
570 ],
571 )
572 .map(|val| (val[3], val[4]))
573 }
574
575 pub fn set_rgb_matrix_color(&self, hue: u8, sat: u8) -> Option<()> {
577 self.hid_command(
578 ViaCommandId::CustomMenuSetValue,
579 vec![
580 ViaChannelId::IdQmkRgbMatrixChannel as u8,
581 ViaQmkRgbMatrixValue::IdQmkRgbMatrixColor as u8,
582 hue,
583 sat,
584 ],
585 )
586 .map(|_| ())
587 }
588
589 pub fn get_led_matrix_brightness(&self) -> Option<u8> {
591 self.hid_command(
592 ViaCommandId::CustomMenuGetValue,
593 vec![
594 ViaChannelId::IdQmkLedMatrixChannel as u8,
595 ViaQmkLedMatrixValue::IdQmkLedMatrixBrightness as u8,
596 ],
597 )
598 .map(|val| val[3])
599 }
600
601 pub fn set_led_matrix_brightness(&self, brightness: u8) -> Option<()> {
603 self.hid_command(
604 ViaCommandId::CustomMenuSetValue,
605 vec![
606 ViaChannelId::IdQmkLedMatrixChannel as u8,
607 ViaQmkLedMatrixValue::IdQmkLedMatrixBrightness as u8,
608 brightness,
609 ],
610 )
611 .map(|_| ())
612 }
613
614 pub fn get_led_matrix_effect(&self) -> Option<u8> {
616 self.hid_command(
617 ViaCommandId::CustomMenuGetValue,
618 vec![
619 ViaChannelId::IdQmkLedMatrixChannel as u8,
620 ViaQmkLedMatrixValue::IdQmkLedMatrixEffect as u8,
621 ],
622 )
623 .map(|val| val[3])
624 }
625
626 pub fn set_led_matrix_effect(&self, effect: u8) -> Option<()> {
628 self.hid_command(
629 ViaCommandId::CustomMenuSetValue,
630 vec![
631 ViaChannelId::IdQmkLedMatrixChannel as u8,
632 ViaQmkLedMatrixValue::IdQmkLedMatrixEffect as u8,
633 effect,
634 ],
635 )
636 .map(|_| ())
637 }
638
639 pub fn get_led_matrix_effect_speed(&self) -> Option<u8> {
641 self.hid_command(
642 ViaCommandId::CustomMenuGetValue,
643 vec![
644 ViaChannelId::IdQmkLedMatrixChannel as u8,
645 ViaQmkLedMatrixValue::IdQmkLedMatrixEffectSpeed as u8,
646 ],
647 )
648 .map(|val| val[3])
649 }
650
651 pub fn set_led_matrix_effect_speed(&self, speed: u8) -> Option<()> {
653 self.hid_command(
654 ViaCommandId::CustomMenuSetValue,
655 vec![
656 ViaChannelId::IdQmkLedMatrixChannel as u8,
657 ViaQmkLedMatrixValue::IdQmkLedMatrixEffectSpeed as u8,
658 speed,
659 ],
660 )
661 .map(|_| ())
662 }
663
664 pub fn save_lighting(&self) -> Option<()> {
666 self.hid_command(ViaCommandId::CustomMenuSave, vec![])
667 .map(|_| ())
668 }
669
670 pub fn get_audio_enabled(&self) -> Option<bool> {
672 self.hid_command(
673 ViaCommandId::CustomMenuGetValue,
674 vec![
675 ViaChannelId::IdQmkAudioChannel as u8,
676 ViaQmkAudioValue::IdQmkAudioEnable as u8,
677 ],
678 )
679 .map(|val| val[3] == 1)
680 }
681
682 pub fn set_audio_enabled(&self, enabled: bool) -> Option<()> {
684 let bytes = vec![
685 ViaChannelId::IdQmkAudioChannel as u8,
686 ViaQmkAudioValue::IdQmkAudioEnable as u8,
687 enabled as u8,
688 ];
689 self.hid_command(ViaCommandId::CustomMenuSetValue, bytes)
690 .map(|_| ())
691 }
692
693 pub fn get_audio_clicky_enabled(&self) -> Option<bool> {
695 self.hid_command(
696 ViaCommandId::CustomMenuGetValue,
697 vec![
698 ViaChannelId::IdQmkAudioChannel as u8,
699 ViaQmkAudioValue::IdQmkAudioClickyEnable as u8,
700 ],
701 )
702 .map(|val| val[3] == 1)
703 }
704
705 pub fn set_audio_clicky_enabled(&self, enabled: bool) -> Option<()> {
707 let bytes = vec![
708 ViaChannelId::IdQmkAudioChannel as u8,
709 ViaQmkAudioValue::IdQmkAudioClickyEnable as u8,
710 enabled as u8,
711 ];
712 self.hid_command(ViaCommandId::CustomMenuSetValue, bytes)
713 .map(|_| ())
714 }
715
716 pub fn get_macro_count(&self) -> Option<u8> {
718 let bytes = vec![];
719 self.hid_command(ViaCommandId::DynamicKeymapMacroGetCount, bytes)
720 .map(|val| val[1])
721 }
722
723 fn get_macro_buffer_size(&self) -> Option<u16> {
724 let bytes = vec![];
725 self.hid_command(ViaCommandId::DynamicKeymapMacroGetBufferSize, bytes)
726 .map(|val| utils::shift_to_16_bit(val[1], val[2]))
727 }
728
729 pub fn get_macro_bytes(&self) -> Option<Vec<u8>> {
731 let macro_buffer_size = self.get_macro_buffer_size()? as usize;
732 let mut all_bytes = Vec::new();
733 for offset in (0..macro_buffer_size).step_by(DATA_BUFFER_SIZE) {
734 let offset_bytes = utils::shift_from_16_bit(offset as u16);
735 let remaining_bytes = macro_buffer_size - offset;
736 let bytes = vec![offset_bytes.0, offset_bytes.1, DATA_BUFFER_SIZE as u8];
737 match self.hid_command(ViaCommandId::DynamicKeymapMacroGetBuffer, bytes) {
738 Some(val) => {
739 if remaining_bytes < DATA_BUFFER_SIZE {
740 all_bytes.extend(val[4..(4 + remaining_bytes)].to_vec())
741 } else {
742 all_bytes.extend(val[4..].to_vec())
743 }
744 }
745 None => return None,
746 }
747 }
748 Some(all_bytes)
749 }
750
751 pub fn set_macro_bytes(&self, data: Vec<u8>) -> Option<()> {
753 let macro_buffer_size = self.get_macro_buffer_size()?;
754 let size = data.len();
755 if size > macro_buffer_size as usize {
756 return None;
757 }
758
759 self.reset_macros()?;
760
761 let last_offset = macro_buffer_size - 1;
762 let last_offset_bytes = utils::shift_from_16_bit(last_offset);
763
764 self.hid_command(
766 ViaCommandId::DynamicKeymapMacroSetBuffer,
767 vec![last_offset_bytes.0, last_offset_bytes.1, 1, 0xff],
768 )?;
769
770 for offset in (0..data.len()).step_by(DATA_BUFFER_SIZE) {
771 let offset_bytes = utils::shift_from_16_bit(offset as u16);
772 let end = std::cmp::min(offset + DATA_BUFFER_SIZE, data.len());
773 let buffer = data[offset..end].to_vec();
774 let mut bytes = vec![offset_bytes.0, offset_bytes.1, buffer.len() as u8];
775 bytes.extend(buffer);
776 self.hid_command(ViaCommandId::DynamicKeymapMacroSetBuffer, bytes)?;
777 }
778
779 self.hid_command(
781 ViaCommandId::DynamicKeymapMacroSetBuffer,
782 vec![last_offset_bytes.0, last_offset_bytes.1, 1, 0x00],
783 )?;
784
785 Some(())
786 }
787
788 pub fn reset_macros(&self) -> Option<()> {
790 let bytes = vec![];
791 self.hid_command(ViaCommandId::DynamicKeymapMacroReset, bytes)
792 .map(|_| ())
793 }
794
795 pub fn reset_eeprom(&self) -> Option<()> {
797 let bytes = vec![];
798 self.hid_command(ViaCommandId::EepromReset, bytes)
799 .map(|_| ())
800 }
801
802 pub fn jump_to_bootloader(&self) -> Option<()> {
804 let bytes = vec![];
805 self.hid_command(ViaCommandId::BootloaderJump, bytes)
806 .map(|_| ())
807 }
808}