1#![cfg_attr(docsrs, feature(doc_cfg))]
8#![warn(missing_docs)]
9
10use std::collections::HashSet;
11use std::error::Error;
12use std::fmt::{Display, Formatter};
13use std::iter::zip;
14use std::str::Utf8Error;
15use std::sync::atomic::{AtomicBool, Ordering};
16use std::sync::RwLock;
17use std::sync::{Arc, Mutex, PoisonError};
18use std::time::Duration;
19
20use crate::images::{convert_image, ImageRect};
21use hidapi::{HidApi, HidDevice, HidError, HidResult};
22use image::{DynamicImage, ImageError};
23
24use crate::info::{is_vendor_familiar, Kind};
25use crate::util::{
26 ajazz153_to_elgato_input, elgato_to_ajazz153, extract_str, flip_key_index, get_feature_report, inverse_key_index, read_button_states, read_data, read_encoder_input, read_lcd_input,
27 send_feature_report, write_data,
28};
29
30pub mod info;
32pub mod util;
34pub mod images;
36
37#[cfg(feature = "async")]
39#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
40pub mod asynchronous;
41#[cfg(feature = "async")]
42#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
43pub use asynchronous::AsyncStreamDeck;
44
45pub fn new_hidapi() -> HidResult<HidApi> {
49 HidApi::new()
50}
51
52pub fn refresh_device_list(hidapi: &mut HidApi) -> HidResult<()> {
54 hidapi.refresh_devices()
55}
56
57pub fn list_devices(hidapi: &HidApi) -> Vec<(Kind, String)> {
61 hidapi
62 .device_list()
63 .filter_map(|d| {
64 if !is_vendor_familiar(&d.vendor_id()) {
65 return None;
66 }
67
68 if let Some(serial) = d.serial_number() {
69 Some((Kind::from_vid_pid(d.vendor_id(), d.product_id())?, serial.to_string()))
70 } else {
71 None
72 }
73 })
74 .collect::<HashSet<_>>()
75 .into_iter()
76 .collect()
77}
78
79#[derive(Clone, Debug)]
81pub enum StreamDeckInput {
82 NoData,
84
85 ButtonStateChange(Vec<bool>),
87
88 EncoderStateChange(Vec<bool>),
90
91 EncoderTwist(Vec<i8>),
93
94 TouchScreenPress(u16, u16),
96
97 TouchScreenLongPress(u16, u16),
99
100 TouchScreenSwipe((u16, u16), (u16, u16)),
102}
103
104impl StreamDeckInput {
105 pub fn is_empty(&self) -> bool {
107 matches!(self, StreamDeckInput::NoData)
108 }
109}
110
111pub struct StreamDeck {
113 kind: Kind,
115 device: HidDevice,
117 image_cache: RwLock<Vec<ImageCache>>,
119 initialized: AtomicBool,
121}
122
123struct ImageCache {
124 key: u8,
125 image_data: Vec<u8>,
126}
127
128impl StreamDeck {
130 pub fn connect(hidapi: &HidApi, kind: Kind, serial: &str) -> Result<StreamDeck, StreamDeckError> {
132 let device = hidapi.open_serial(kind.vendor_id(), kind.product_id(), serial)?;
133
134 Ok(StreamDeck {
135 kind,
136 device,
137 image_cache: RwLock::new(vec![]),
138 initialized: false.into(),
139 })
140 }
141}
142
143impl StreamDeck {
145 pub fn kind(&self) -> Kind {
147 self.kind
148 }
149
150 pub fn manufacturer(&self) -> Result<String, StreamDeckError> {
152 Ok(self.device.get_manufacturer_string()?.unwrap_or_else(|| "Unknown".to_string()))
153 }
154
155 pub fn product(&self) -> Result<String, StreamDeckError> {
157 Ok(self.device.get_product_string()?.unwrap_or_else(|| "Unknown".to_string()))
158 }
159
160 pub fn serial_number(&self) -> Result<String, StreamDeckError> {
162 match self.kind {
163 Kind::Akp153 | Kind::Akp153E | Kind::Akp815 | Kind::MiraBoxHSV293S => {
164 let serial = self.device.get_serial_number_string()?;
165 match serial {
166 Some(serial) => {
167 if serial.is_empty() {
168 Ok("Unknown".to_string())
169 } else {
170 Ok(serial)
171 }
172 }
173 None => Ok("Unknown".to_string()),
174 }
175 }
176
177 Kind::Original | Kind::Mini => {
178 let bytes = get_feature_report(&self.device, 0x03, 17)?;
179 Ok(extract_str(&bytes[5..])?)
180 }
181
182 Kind::MiniMk2 => {
183 let bytes = get_feature_report(&self.device, 0x03, 32)?;
184 Ok(extract_str(&bytes[5..])?)
185 }
186
187 _ => {
188 let bytes = get_feature_report(&self.device, 0x06, 32)?;
189 Ok(extract_str(&bytes[2..])?)
190 }
191 }
192 .map(|s| s.replace('\u{0001}', ""))
193 }
194
195 pub fn firmware_version(&self) -> Result<String, StreamDeckError> {
197 match self.kind {
198 Kind::Original | Kind::Mini | Kind::MiniMk2 => {
199 let bytes = get_feature_report(&self.device, 0x04, 17)?;
200 Ok(extract_str(&bytes[5..])?)
201 }
202
203 Kind::Akp153 | Kind::Akp153E | Kind::Akp815 | Kind::MiraBoxHSV293S => {
204 let bytes = get_feature_report(&self.device, 0x01, 20)?;
205 Ok(extract_str(&bytes[0..])?)
206 }
207
208 _ => {
209 let bytes = get_feature_report(&self.device, 0x05, 32)?;
210 Ok(extract_str(&bytes[6..])?)
211 }
212 }
213 }
214
215 fn initialize(&self) -> Result<(), StreamDeckError> {
217 if self.initialized.load(Ordering::Acquire) {
218 return Ok(());
219 }
220 self.initialized.store(true, Ordering::Release);
221 match self.kind {
222 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => {
223 let mut buf = vec![0x00, 0x43, 0x52, 0x54, 0x00, 0x00, 0x44, 0x49, 0x53];
224 buf.extend(vec![0u8; 513 - buf.len()]);
225 write_data(&self.device, buf.as_slice())?;
226
227 let mut buf = vec![0x00, 0x43, 0x52, 0x54, 0x00, 0x00, 0x4c, 0x49, 0x47, 0x00, 0x00, 0x00, 0x00];
228 buf.extend(vec![0u8; 513 - buf.len()]);
229 write_data(&self.device, buf.as_slice())?;
230 }
231
232 _ => {}
233 }
234 Ok(())
235 }
236
237 pub fn read_input(&self, timeout: Option<Duration>) -> Result<StreamDeckInput, StreamDeckError> {
239 self.initialize()?;
240 match &self.kind {
241 Kind::Plus => {
242 let data = read_data(&self.device, 14.max(5 + self.kind.encoder_count() as usize), timeout)?;
243
244 if data[0] == 0 {
245 return Ok(StreamDeckInput::NoData);
246 }
247
248 match &data[1] {
249 0x0 => Ok(StreamDeckInput::ButtonStateChange(read_button_states(&self.kind, &data))),
250
251 0x2 => Ok(read_lcd_input(&data)?),
252
253 0x3 => Ok(read_encoder_input(&self.kind, &data)?),
254
255 _ => Err(StreamDeckError::BadData),
256 }
257 }
258
259 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => {
260 let data = read_data(&self.device, 512, timeout)?;
261
262 if data[0] == 0 {
263 return Ok(StreamDeckInput::NoData);
264 }
265
266 let mut states = vec![0x01];
267 states.extend(vec![0u8; (self.kind.key_count() + 1) as usize]);
268
269 if data[9] != 0 {
270 let key = if self.kind == Kind::Akp815 {
271 inverse_key_index(&self.kind, data[9] - 1)
272 } else {
273 ajazz153_to_elgato_input(&self.kind, data[9] - 1)
274 };
275
276 states[(key + 1) as usize] = 0x1u8;
277 }
278
279 Ok(StreamDeckInput::ButtonStateChange(read_button_states(&self.kind, &states)))
280 }
281
282 _ => {
283 let data = match self.kind {
284 Kind::Original | Kind::Mini | Kind::MiniMk2 => read_data(&self.device, 1 + self.kind.key_count() as usize, timeout),
285 _ => read_data(&self.device, 4 + self.kind.key_count() as usize + self.kind.touchpoint_count() as usize, timeout),
286 }?;
287
288 if data[0] == 0 {
289 return Ok(StreamDeckInput::NoData);
290 }
291
292 Ok(StreamDeckInput::ButtonStateChange(read_button_states(&self.kind, &data)))
293 }
294 }
295 }
296
297 pub fn reset(&self) -> Result<(), StreamDeckError> {
299 self.initialize()?;
300 match self.kind {
301 Kind::Original | Kind::Mini | Kind::MiniMk2 => {
302 let mut buf = vec![0x0B, 0x63];
303
304 buf.extend(vec![0u8; 15]);
305
306 Ok(send_feature_report(&self.device, buf.as_slice())?)
307 }
308
309 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => {
310 self.set_brightness(100)?;
311 self.clear_button_image(0xff)?;
312 Ok(())
313 }
314
315 _ => {
316 let mut buf = vec![0x03, 0x02];
317
318 buf.extend(vec![0u8; 30]);
319
320 Ok(send_feature_report(&self.device, buf.as_slice())?)
321 }
322 }
323 }
324
325 pub fn set_brightness(&self, percent: u8) -> Result<(), StreamDeckError> {
327 self.initialize()?;
328 let percent = percent.clamp(0, 100);
329
330 match self.kind {
331 Kind::Original | Kind::Mini | Kind::MiniMk2 => {
332 let mut buf = vec![0x05, 0x55, 0xaa, 0xd1, 0x01, percent];
333
334 buf.extend(vec![0u8; 11]);
335
336 Ok(send_feature_report(&self.device, buf.as_slice())?)
337 }
338
339 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => {
340 let mut buf = vec![0x00, 0x43, 0x52, 0x54, 0x00, 0x00, 0x4c, 0x49, 0x47, 0x00, 0x00, percent];
341
342 buf.extend(vec![0u8; 513 - buf.len()]);
343
344 write_data(&self.device, buf.as_slice())?;
345
346 Ok(())
347 }
348
349 _ => {
350 let mut buf = vec![0x03, 0x08, percent];
351
352 buf.extend(vec![0u8; 29]);
353
354 Ok(send_feature_report(&self.device, buf.as_slice())?)
355 }
356 }
357 }
358
359 fn send_image(&self, key: u8, image_data: &[u8]) -> Result<(), StreamDeckError> {
360 if key >= self.kind.key_count() {
361 return Err(StreamDeckError::InvalidKeyIndex);
362 }
363
364 let key = if let Kind::Original = self.kind {
365 flip_key_index(&self.kind, key)
366 } else if let Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::MiraBoxHSV293S = self.kind {
367 elgato_to_ajazz153(&self.kind, key)
368 } else if let Kind::Akp815 = self.kind {
369 inverse_key_index(&self.kind, key)
370 } else {
371 key
372 };
373
374 if !self.kind.is_visual() {
375 return Err(StreamDeckError::NoScreen);
376 }
377
378 match self.kind {
379 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => {
380 let mut buf = vec![
381 0x00,
382 0x43,
383 0x52,
384 0x54,
385 0x00,
386 0x00,
387 0x42,
388 0x41,
389 0x54,
390 0x00,
391 0x00,
392 (image_data.len() >> 8) as u8,
393 image_data.len() as u8,
394 key + 1,
395 ];
396
397 buf.extend(vec![0u8; 513 - buf.len()]);
398
399 write_data(&self.device, buf.as_slice())?;
400 }
401
402 _ => {}
403 }
404
405 self.write_image_data_reports(
406 image_data,
407 WriteImageParameters::for_key(self.kind, image_data.len()),
408 |page_number, this_length, last_package| match self.kind {
409 Kind::Original => vec![0x02, 0x01, (page_number + 1) as u8, 0, if last_package { 1 } else { 0 }, key + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
410
411 Kind::Mini | Kind::MiniMk2 => vec![0x02, 0x01, page_number as u8, 0, if last_package { 1 } else { 0 }, key + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
412
413 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => vec![0x00],
414
415 _ => vec![
416 0x02,
417 0x07,
418 key,
419 if last_package { 1 } else { 0 },
420 (this_length & 0xff) as u8,
421 (this_length >> 8) as u8,
422 (page_number & 0xff) as u8,
423 (page_number >> 8) as u8,
424 ],
425 },
426 )?;
427 Ok(())
428 }
429
430 pub fn write_image(&self, key: u8, image_data: &[u8]) -> Result<(), StreamDeckError> {
433 let cache_entry = ImageCache {
434 key,
435 image_data: image_data.to_vec(), };
437
438 self.image_cache.write()?.push(cache_entry);
439
440 Ok(())
441 }
442
443 pub fn write_lcd(&self, x: u16, y: u16, rect: &ImageRect) -> Result<(), StreamDeckError> {
446 self.initialize()?;
447 match self.kind {
448 Kind::Plus => (),
449 _ => return Err(StreamDeckError::UnsupportedOperation),
450 }
451
452 self.write_image_data_reports(
453 rect.data.as_slice(),
454 WriteImageParameters {
455 image_report_length: 1024,
456 image_report_payload_length: 1024 - 16,
457 },
458 |page_number, this_length, last_package| {
459 vec![
460 0x02,
461 0x0c,
462 (x & 0xff) as u8,
463 (x >> 8) as u8,
464 (y & 0xff) as u8,
465 (y >> 8) as u8,
466 (rect.w & 0xff) as u8,
467 (rect.w >> 8) as u8,
468 (rect.h & 0xff) as u8,
469 (rect.h >> 8) as u8,
470 if last_package { 1 } else { 0 },
471 (page_number & 0xff) as u8,
472 (page_number >> 8) as u8,
473 (this_length & 0xff) as u8,
474 (this_length >> 8) as u8,
475 0,
476 ]
477 },
478 )
479 }
480
481 pub fn write_lcd_fill(&self, image_data: &[u8]) -> Result<(), StreamDeckError> {
490 self.initialize()?;
491 match self.kind {
492 Kind::Neo => self.write_image_data_reports(
493 image_data,
494 WriteImageParameters {
495 image_report_length: 1024,
496 image_report_payload_length: 1024 - 8,
497 },
498 |page_number, this_length, last_package| {
499 vec![
500 0x02,
501 0x0b,
502 0,
503 if last_package { 1 } else { 0 },
504 (this_length & 0xff) as u8,
505 (this_length >> 8) as u8,
506 (page_number & 0xff) as u8,
507 (page_number >> 8) as u8,
508 ]
509 },
510 ),
511
512 Kind::Plus => {
513 let (w, h) = self.kind.lcd_strip_size().unwrap();
514
515 self.write_image_data_reports(
516 image_data,
517 WriteImageParameters {
518 image_report_length: 1024,
519 image_report_payload_length: 1024 - 16,
520 },
521 |page_number, this_length, last_package| {
522 vec![
523 0x02,
524 0x0c,
525 0,
526 0,
527 0,
528 0,
529 (w & 0xff) as u8,
530 (w >> 8) as u8,
531 (h & 0xff) as u8,
532 (h >> 8) as u8,
533 if last_package { 1 } else { 0 },
534 (page_number & 0xff) as u8,
535 (page_number >> 8) as u8,
536 (this_length & 0xff) as u8,
537 (this_length >> 8) as u8,
538 0,
539 ]
540 },
541 )
542 }
543
544 _ => Err(StreamDeckError::UnsupportedOperation),
545 }
546 }
547
548 pub fn clear_button_image(&self, key: u8) -> Result<(), StreamDeckError> {
551 self.initialize()?;
552 match self.kind {
553 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => {
554 let key = if self.kind == Kind::Akp815 {
555 inverse_key_index(&self.kind, key)
556 } else {
557 elgato_to_ajazz153(&self.kind, key)
558 };
559
560 let mut buf = vec![0x00, 0x43, 0x52, 0x54, 0x00, 0x00, 0x43, 0x4c, 0x45, 0x00, 0x00, 0x00, if key == 0xff { 0xff } else { key + 1 }];
561
562 buf.extend(vec![0u8; 513 - buf.len()]);
563
564 write_data(&self.device, buf.as_slice())?;
565
566 Ok(())
567 }
568
569 _ => Ok(self.send_image(key, &self.kind.blank_image())?),
570 }
571 }
572
573 pub fn clear_all_button_images(&self) -> Result<(), StreamDeckError> {
576 self.initialize()?;
577 match self.kind {
578 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => self.clear_button_image(0xff),
579 _ => {
580 for i in 0..self.kind.key_count() {
581 self.clear_button_image(i)?
582 }
583 Ok(())
584 }
585 }
586 }
587
588 pub fn set_button_image(&self, key: u8, image: DynamicImage) -> Result<(), StreamDeckError> {
591 self.initialize()?;
592 let image_data = convert_image(self.kind, image)?;
593 self.write_image(key, &image_data)?;
594 Ok(())
595 }
596
597 pub fn set_logo_image(&self, image: DynamicImage) -> Result<(), StreamDeckError> {
599 self.initialize()?;
600 match self.kind {
601 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => (),
602 _ => return Err(StreamDeckError::UnsupportedOperation),
603 }
604
605 if self.kind.lcd_strip_size().is_none() {
606 return Err(StreamDeckError::UnsupportedOperation);
607 }
608 let mut buf = vec![0x00, 0x43, 0x52, 0x54, 0x00, 0x00, 0x4c, 0x4f, 0x47, 0x00, 0x12, 0xc3, 0xc0, 0x01];
610
611 buf.extend(vec![0u8; 513 - buf.len()]);
612
613 write_data(&self.device, buf.as_slice())?;
614
615 let mut image_buffer: DynamicImage = DynamicImage::new_rgb8(854, 480);
616
617 let ratio = 854.0 / 480.0;
618
619 let mode = "cover";
620
621 match mode {
622 "contain" => {
623 let (image_w, image_h) = (image.width(), image.height());
624 let image_ratio = image_w as f32 / image_h as f32;
625
626 let (ws, hs) = if image_ratio > ratio {
627 (854, (854.0 / image_ratio) as u32)
628 } else {
629 ((480.0 * image_ratio) as u32, 480)
630 };
631
632 let resized_image = image.resize(ws, hs, image::imageops::FilterType::Nearest);
633 image::imageops::overlay(
634 &mut image_buffer,
635 &resized_image,
636 ((854 - resized_image.width()) / 2) as i64,
637 ((480 - resized_image.height()) / 2) as i64,
638 );
639 }
640 "cover" => {
641 let resized_image = image.resize_to_fill(854, 480, image::imageops::FilterType::Nearest);
642 image::imageops::overlay(
643 &mut image_buffer,
644 &resized_image,
645 ((854 - resized_image.width()) / 2) as i64,
646 ((480 - resized_image.height()) / 2) as i64,
647 );
648 }
649 _ => {
650 let (image_w, image_h) = (image.width(), image.height());
651 let image_ratio = image_w as f32 / image_h as f32;
652
653 let (ws, hs) = if image_ratio > ratio {
654 ((480.0 * image_ratio) as u32, 480)
655 } else {
656 (854, (854.0 / image_ratio) as u32)
657 };
658
659 let resized_image = image.resize(ws, hs, image::imageops::FilterType::Nearest);
660 image::imageops::overlay(
661 &mut image_buffer,
662 &resized_image,
663 ((854 - resized_image.width()) / 2) as i64,
664 ((480 - resized_image.height()) / 2) as i64,
665 );
666 }
667 }
668
669 let mut image_data = image_buffer.rotate90().fliph().flipv().into_rgb8().to_vec();
670 for x in (0..image_data.len()).step_by(3) {
671 (image_data[x], image_data[x + 2]) = (image_data[x + 2], image_data[x])
672 }
673
674 let image_report_length = match self.kind {
675 Kind::Original => 8191,
676 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => 513,
677 _ => 1024,
678 };
679
680 let image_report_header_length = match self.kind {
681 Kind::Original | Kind::Mini | Kind::MiniMk2 => 16,
682 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => 1,
683 _ => 8,
684 };
685
686 let image_report_payload_length = match self.kind {
687 Kind::Original => image_data.len() / 2,
688 _ => image_report_length - image_report_header_length,
689 };
690
691 let mut page_number = 0;
692 let mut bytes_remaining = image_data.len();
693
694 while bytes_remaining > 0 {
695 let this_length = bytes_remaining.min(image_report_payload_length);
696 let bytes_sent = page_number * image_report_payload_length;
697
698 let mut buf: Vec<u8> = vec![0x00];
700
701 buf.extend(&image_data[bytes_sent..bytes_sent + this_length]);
703
704 buf.extend(vec![0u8; image_report_length - buf.len()]);
706
707 write_data(&self.device, &buf)?;
708
709 bytes_remaining -= this_length;
710 page_number += 1;
711 }
712
713 Ok(())
714 }
715
716 pub fn set_touchpoint_color(&self, point: u8, red: u8, green: u8, blue: u8) -> Result<(), StreamDeckError> {
718 self.initialize()?;
719 if point >= self.kind.touchpoint_count() {
720 return Err(StreamDeckError::InvalidTouchPointIndex);
721 }
722
723 let mut buf = vec![0x03, 0x06];
724
725 let touchpoint_index: u8 = point + self.kind.key_count();
726 buf.extend(vec![touchpoint_index]);
727 buf.extend(vec![red, green, blue]);
728
729 Ok(send_feature_report(&self.device, buf.as_slice())?)
730 }
731
732 pub fn sleep(&self) -> Result<(), StreamDeckError> {
734 self.initialize()?;
735 match self.kind {
736 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => {
737 let mut buf = vec![0x00, 0x43, 0x52, 0x54, 0x00, 0x00, 0x48, 0x41, 0x4e];
738
739 buf.extend(vec![0u8; 513 - buf.len()]);
740
741 write_data(&self.device, buf.as_slice())?;
742
743 Ok(())
744 }
745
746 _ => Err(StreamDeckError::UnsupportedOperation),
747 }
748 }
749
750 pub fn keep_alive(&self) -> Result<(), StreamDeckError> {
752 self.initialize()?;
753 match self.kind {
754 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => {
755 let mut buf = vec![0x00, 0x43, 0x52, 0x54, 0x00, 0x00, 0x43, 0x4F, 0x4E, 0x4E, 0x45, 0x43, 0x54];
756 buf.extend(vec![0u8; 513 - buf.len()]);
757 write_data(&self.device, buf.as_slice())?;
758 Ok(())
759 }
760
761 _ => Err(StreamDeckError::UnsupportedOperation),
762 }
763 }
764
765 pub fn shutdown(&self) -> Result<(), StreamDeckError> {
767 self.initialize()?;
768 match self.kind {
769 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 => {
770 let mut buf = vec![0x00, 0x43, 0x52, 0x54, 0x00, 0x00, 0x43, 0x4c, 0x45, 0x00, 0x00, 0x44, 0x43];
771 buf.extend(vec![0u8; 513 - buf.len()]);
772 write_data(&self.device, buf.as_slice())?;
773
774 let mut buf = vec![0x00, 0x43, 0x52, 0x54, 0x00, 0x00, 0x48, 0x41, 0x4E];
775 buf.extend(vec![0u8; 513 - buf.len()]);
776 write_data(&self.device, buf.as_slice())?;
777
778 Ok(())
779 }
780
781 _ => Err(StreamDeckError::UnsupportedOperation),
782 }
783 }
784
785 pub fn flush(&self) -> Result<(), StreamDeckError> {
787 self.initialize()?;
788
789 if self.image_cache.write()?.len() == 0 {
790 return Ok(());
791 }
792
793 for image in self.image_cache.read()?.iter() {
794 self.send_image(image.key, &image.image_data)?;
795 }
796
797 match self.kind {
798 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => {
799 let mut buf = vec![0x00, 0x43, 0x52, 0x54, 0x00, 0x00, 0x53, 0x54, 0x50];
800
801 buf.extend(vec![0u8; 513 - buf.len()]);
802
803 write_data(&self.device, buf.as_slice())?;
804 }
805
806 _ => {}
807 }
808
809 self.image_cache.write()?.clear();
810
811 Ok(())
812 }
813
814 pub fn get_reader(self: &Arc<Self>) -> Arc<DeviceStateReader> {
816 #[allow(clippy::arc_with_non_send_sync)]
817 Arc::new(DeviceStateReader {
818 device: self.clone(),
819 states: Mutex::new(DeviceState {
820 buttons: vec![false; self.kind.key_count() as usize + self.kind.touchpoint_count() as usize],
821 encoders: vec![false; self.kind.encoder_count() as usize],
822 }),
823 })
824 }
825
826 fn write_image_data_reports<T>(&self, image_data: &[u8], parameters: WriteImageParameters, header_fn: T) -> Result<(), StreamDeckError>
827 where
828 T: Fn(usize, usize, bool) -> Vec<u8>,
829 {
830 let image_report_length = parameters.image_report_length;
831 let image_report_payload_length = parameters.image_report_payload_length;
832
833 let mut page_number = 0;
834 let mut bytes_remaining = image_data.len();
835
836 while bytes_remaining > 0 {
837 let this_length = bytes_remaining.min(image_report_payload_length);
838 let bytes_sent = page_number * image_report_payload_length;
839
840 let mut buf: Vec<u8> = header_fn(page_number, this_length, this_length == bytes_remaining);
842
843 buf.extend(&image_data[bytes_sent..bytes_sent + this_length]);
844
845 buf.extend(vec![0u8; image_report_length - buf.len()]);
847
848 write_data(&self.device, &buf)?;
849
850 bytes_remaining -= this_length;
851 page_number += 1;
852 }
853
854 Ok(())
855 }
856}
857
858#[derive(Clone, Copy)]
859struct WriteImageParameters {
860 pub image_report_length: usize,
861 pub image_report_payload_length: usize,
862}
863
864impl WriteImageParameters {
865 pub fn for_key(kind: Kind, image_data_len: usize) -> Self {
866 let image_report_length = match kind {
867 Kind::Original => 8191,
868 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => 513,
869 _ => 1024,
870 };
871
872 let image_report_header_length = match kind {
873 Kind::Original | Kind::Mini | Kind::MiniMk2 => 16,
874 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => 1,
875 _ => 8,
876 };
877
878 let image_report_payload_length = match kind {
879 Kind::Original => image_data_len / 2,
880 _ => image_report_length - image_report_header_length,
881 };
882
883 Self {
884 image_report_length,
885 image_report_payload_length,
886 }
887 }
888}
889
890#[derive(Debug)]
892pub enum StreamDeckError {
893 HidError(HidError),
895
896 Utf8Error(Utf8Error),
898
899 ImageError(ImageError),
901
902 #[cfg(feature = "async")]
903 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
904 JoinError(tokio::task::JoinError),
906
907 PoisonError,
909
910 NoScreen,
912
913 InvalidKeyIndex,
915
916 InvalidTouchPointIndex,
918
919 UnrecognizedPID,
921
922 UnsupportedOperation,
924
925 BadData,
927}
928
929impl Display for StreamDeckError {
930 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
931 write!(f, "{:?}", self)
932 }
933}
934
935impl Error for StreamDeckError {}
936
937impl From<HidError> for StreamDeckError {
938 fn from(e: HidError) -> Self {
939 Self::HidError(e)
940 }
941}
942
943impl From<Utf8Error> for StreamDeckError {
944 fn from(e: Utf8Error) -> Self {
945 Self::Utf8Error(e)
946 }
947}
948
949impl From<ImageError> for StreamDeckError {
950 fn from(e: ImageError) -> Self {
951 Self::ImageError(e)
952 }
953}
954
955#[cfg(feature = "async")]
956impl From<tokio::task::JoinError> for StreamDeckError {
957 fn from(e: tokio::task::JoinError) -> Self {
958 Self::JoinError(e)
959 }
960}
961
962impl<T> From<PoisonError<T>> for StreamDeckError {
963 fn from(_value: PoisonError<T>) -> Self {
964 Self::PoisonError
965 }
966}
967
968#[derive(Copy, Clone, Debug, Hash)]
970pub enum DeviceStateUpdate {
971 ButtonDown(u8),
973
974 ButtonUp(u8),
976
977 EncoderDown(u8),
979
980 EncoderUp(u8),
982
983 EncoderTwist(u8, i8),
985
986 TouchPointDown(u8),
988
989 TouchPointUp(u8),
991
992 TouchScreenPress(u16, u16),
994
995 TouchScreenLongPress(u16, u16),
997
998 TouchScreenSwipe((u16, u16), (u16, u16)),
1000}
1001
1002#[derive(Default)]
1003struct DeviceState {
1004 pub buttons: Vec<bool>,
1006 pub encoders: Vec<bool>,
1007}
1008
1009pub struct DeviceStateReader {
1011 device: Arc<StreamDeck>,
1012 states: Mutex<DeviceState>,
1013}
1014
1015impl DeviceStateReader {
1016 pub fn read(&self, timeout: Option<Duration>) -> Result<Vec<DeviceStateUpdate>, StreamDeckError> {
1018 let input = self.device.read_input(timeout)?;
1019 let mut my_states = self.states.lock()?;
1020
1021 let mut updates = vec![];
1022
1023 match input {
1024 StreamDeckInput::ButtonStateChange(buttons) => {
1025 for (index, (their, mine)) in zip(buttons.iter(), my_states.buttons.iter()).enumerate() {
1026 match self.device.kind {
1027 Kind::Akp153 | Kind::Akp153E | Kind::Akp153R | Kind::Akp815 | Kind::MiraBoxHSV293S => {
1028 if *their {
1029 updates.push(DeviceStateUpdate::ButtonDown(index as u8));
1030 updates.push(DeviceStateUpdate::ButtonUp(index as u8));
1031 }
1032 }
1033 _ => {
1034 if their != mine {
1035 let key_count = self.device.kind.key_count();
1036 if index < key_count as usize {
1037 if *their {
1038 updates.push(DeviceStateUpdate::ButtonDown(index as u8));
1039 } else {
1040 updates.push(DeviceStateUpdate::ButtonUp(index as u8));
1041 }
1042 } else if *their {
1043 updates.push(DeviceStateUpdate::TouchPointDown(index as u8 - key_count));
1044 } else {
1045 updates.push(DeviceStateUpdate::TouchPointUp(index as u8 - key_count));
1046 }
1047 }
1048 }
1049 }
1050 }
1051
1052 my_states.buttons = buttons;
1053 }
1054
1055 StreamDeckInput::EncoderStateChange(encoders) => {
1056 for (index, (their, mine)) in zip(encoders.iter(), my_states.encoders.iter()).enumerate() {
1057 if *their != *mine {
1058 if *their {
1059 updates.push(DeviceStateUpdate::EncoderDown(index as u8));
1060 } else {
1061 updates.push(DeviceStateUpdate::EncoderUp(index as u8));
1062 }
1063 }
1064 }
1065
1066 my_states.encoders = encoders;
1067 }
1068
1069 StreamDeckInput::EncoderTwist(twist) => {
1070 for (index, change) in twist.iter().enumerate() {
1071 if *change != 0 {
1072 updates.push(DeviceStateUpdate::EncoderTwist(index as u8, *change));
1073 }
1074 }
1075 }
1076
1077 StreamDeckInput::TouchScreenPress(x, y) => {
1078 updates.push(DeviceStateUpdate::TouchScreenPress(x, y));
1079 }
1080
1081 StreamDeckInput::TouchScreenLongPress(x, y) => {
1082 updates.push(DeviceStateUpdate::TouchScreenLongPress(x, y));
1083 }
1084
1085 StreamDeckInput::TouchScreenSwipe(s, e) => {
1086 updates.push(DeviceStateUpdate::TouchScreenSwipe(s, e));
1087 }
1088
1089 _ => {}
1090 }
1091
1092 drop(my_states);
1093
1094 Ok(updates)
1095 }
1096}