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