elgato_streamdeck/
asynchronous.rs1use std::iter::zip;
5use std::sync::Arc;
6use std::time::Duration;
7
8use hidapi::{HidApi, HidResult};
9use image::DynamicImage;
10use tokio::sync::Mutex;
11use tokio::task::block_in_place;
12use tokio::time::sleep;
13
14use crate::{DeviceState, DeviceStateUpdate, Kind, list_devices, StreamDeck, StreamDeckError, StreamDeckInput};
15use crate::images::{convert_image_async, ImageRect};
16
17pub fn refresh_device_list_async(hidapi: &mut HidApi) -> HidResult<()> {
19 block_in_place(move || hidapi.refresh_devices())
20}
21
22pub fn list_devices_async(hidapi: &HidApi) -> Vec<(Kind, String)> {
27 block_in_place(move || list_devices(hidapi))
28}
29
30#[derive(Clone)]
33pub struct AsyncStreamDeck {
34 kind: Kind,
35 device: Arc<Mutex<StreamDeck>>,
36}
37
38impl AsyncStreamDeck {
40 pub fn connect(hidapi: &HidApi, kind: Kind, serial: &str) -> Result<AsyncStreamDeck, StreamDeckError> {
42 let device = block_in_place(move || StreamDeck::connect(hidapi, kind, serial))?;
43
44 Ok(AsyncStreamDeck {
45 kind,
46 device: Arc::new(Mutex::new(device)),
47 })
48 }
49}
50
51impl AsyncStreamDeck {
53 pub fn kind(&self) -> Kind {
55 self.kind
56 }
57
58 pub async fn manufacturer(&self) -> Result<String, StreamDeckError> {
60 let device = self.device.lock().await;
61 block_in_place(move || device.manufacturer())
62 }
63
64 pub async fn product(&self) -> Result<String, StreamDeckError> {
66 let device = self.device.lock().await;
67 block_in_place(move || device.product())
68 }
69
70 pub async fn serial_number(&self) -> Result<String, StreamDeckError> {
72 let device = self.device.lock().await;
73 block_in_place(move || device.serial_number())
74 }
75
76 pub async fn firmware_version(&self) -> Result<String, StreamDeckError> {
78 let device = self.device.lock().await;
79 block_in_place(move || device.firmware_version())
80 }
81
82 pub async fn read_input(&self, poll_rate: f32) -> Result<StreamDeckInput, StreamDeckError> {
85 loop {
86 let device = self.device.lock().await;
87 let data = block_in_place(move || device.read_input(None))?;
88
89 if !data.is_empty() {
90 return Ok(data);
91 }
92
93 sleep(Duration::from_secs_f32(1.0 / poll_rate)).await;
94 }
95 }
96
97 pub async fn reset(&self) -> Result<(), StreamDeckError> {
99 let device = self.device.lock().await;
100 block_in_place(move || device.reset())
101 }
102
103 pub async fn set_brightness(&self, percent: u8) -> Result<(), StreamDeckError> {
105 let device = self.device.lock().await;
106 block_in_place(move || device.set_brightness(percent))
107 }
108
109 pub async fn write_image(&self, key: u8, image_data: &[u8]) -> Result<(), StreamDeckError> {
112 let device = self.device.lock().await;
113 block_in_place(move || device.write_image(key, image_data))
114 }
115
116 pub async fn write_lcd(&self, x: u16, y: u16, rect: &ImageRect) -> Result<(), StreamDeckError> {
119 let device = self.device.lock().await;
120 block_in_place(move || device.write_lcd(x, y, rect))
121 }
122
123 pub async fn write_lcd_fill(&self, image_data: &[u8]) -> Result<(), StreamDeckError> {
132 let device = self.device.lock().await;
133 block_in_place(move || device.write_lcd_fill(image_data))
134 }
135
136 pub async fn clear_button_image(&self, key: u8) -> Result<(), StreamDeckError> {
139 let device = self.device.lock().await;
140 block_in_place(move || device.clear_button_image(key))
141 }
142
143 pub async fn clear_all_button_images(&self) -> Result<(), StreamDeckError> {
146 let device = self.device.lock().await;
147 block_in_place(move || device.clear_all_button_images())
148 }
149
150 pub async fn set_button_image(&self, key: u8, image: DynamicImage) -> Result<(), StreamDeckError> {
153 let image = convert_image_async(self.kind, image)?;
154
155 let device = self.device.lock().await;
156 block_in_place(move || device.write_image(key, &image))
157 }
158
159 pub async fn set_touchpoint_color(&self, point: u8, red: u8, green: u8, blue: u8) -> Result<(), StreamDeckError> {
161 let device = self.device.lock().await;
162 block_in_place(move || device.set_touchpoint_color(point, red, green, blue))
163 }
164
165 pub async fn flush(&self) -> Result<(), StreamDeckError> {
167 let device = self.device.lock().await;
168 block_in_place(move || device.flush())
169 }
170
171 pub fn get_reader(&self) -> Arc<AsyncDeviceStateReader> {
173 Arc::new(AsyncDeviceStateReader {
174 device: self.clone(),
175 states: Mutex::new(DeviceState {
176 buttons: vec![false; self.kind.key_count() as usize + self.kind.touchpoint_count() as usize],
177 encoders: vec![false; self.kind.encoder_count() as usize],
178 }),
179 })
180 }
181}
182
183pub struct AsyncDeviceStateReader {
185 device: AsyncStreamDeck,
186 states: Mutex<DeviceState>,
187}
188
189impl AsyncDeviceStateReader {
190 pub async fn read(&self, poll_rate: f32) -> Result<Vec<DeviceStateUpdate>, StreamDeckError> {
192 let input = self.device.read_input(poll_rate).await?;
193 let mut my_states = self.states.lock().await;
194
195 let mut updates = vec![];
196
197 match input {
198 StreamDeckInput::ButtonStateChange(buttons) => {
199 for (index, (their, mine)) in zip(buttons.iter(), my_states.buttons.iter()).enumerate() {
200 if *their != *mine {
201 if index < self.device.kind.key_count() as usize {
202 if *their {
203 updates.push(DeviceStateUpdate::ButtonDown(index as u8));
204 } else {
205 updates.push(DeviceStateUpdate::ButtonUp(index as u8));
206 }
207 } else if *their {
208 updates.push(DeviceStateUpdate::TouchPointDown(index as u8 - self.device.kind.key_count()));
209 } else {
210 updates.push(DeviceStateUpdate::TouchPointUp(index as u8 - self.device.kind.key_count()));
211 }
212 }
213 }
214
215 my_states.buttons = buttons;
216 }
217
218 StreamDeckInput::EncoderStateChange(encoders) => {
219 for (index, (their, mine)) in zip(encoders.iter(), my_states.encoders.iter()).enumerate() {
220 if *their != *mine {
221 if *their {
222 updates.push(DeviceStateUpdate::EncoderDown(index as u8));
223 } else {
224 updates.push(DeviceStateUpdate::EncoderUp(index as u8));
225 }
226 }
227 }
228
229 my_states.encoders = encoders;
230 }
231
232 StreamDeckInput::EncoderTwist(twist) => {
233 for (index, change) in twist.iter().enumerate() {
234 if *change != 0 {
235 updates.push(DeviceStateUpdate::EncoderTwist(index as u8, *change));
236 }
237 }
238 }
239
240 StreamDeckInput::TouchScreenPress(x, y) => {
241 updates.push(DeviceStateUpdate::TouchScreenPress(x, y));
242 }
243
244 StreamDeckInput::TouchScreenLongPress(x, y) => {
245 updates.push(DeviceStateUpdate::TouchScreenLongPress(x, y));
246 }
247
248 StreamDeckInput::TouchScreenSwipe(s, e) => {
249 updates.push(DeviceStateUpdate::TouchScreenSwipe(s, e));
250 }
251
252 _ => {}
253 }
254
255 drop(my_states);
256
257 Ok(updates)
258 }
259}