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, only_elgato: bool) -> Vec<(Kind, String)> {
27 block_in_place(move || list_devices(hidapi, only_elgato))
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_logo_image(&self, image: DynamicImage) -> Result<(), StreamDeckError> {
161 let device = self.device.lock().await;
162 block_in_place(move || device.set_logo_image(image))
163 }
164
165 pub async fn set_touchpoint_color(&self, point: u8, red: u8, green: u8, blue: u8) -> Result<(), StreamDeckError> {
167 let device = self.device.lock().await;
168 block_in_place(move || device.set_touchpoint_color(point, red, green, blue))
169 }
170
171 pub async fn sleep(&self) -> Result<(), StreamDeckError> {
173 let device = self.device.lock().await;
174 block_in_place(move || device.sleep())
175 }
176
177 pub async fn keep_alive(&self) -> Result<(), StreamDeckError> {
179 let device = self.device.lock().await;
180 block_in_place(move || device.keep_alive())
181 }
182
183 pub async fn shutdown(&self) -> Result<(), StreamDeckError> {
185 let device = self.device.lock().await;
186 block_in_place(move || device.shutdown())
187 }
188
189 pub async fn flush(&self) -> Result<(), StreamDeckError> {
191 let device = self.device.lock().await;
192 block_in_place(move || device.flush())
193 }
194
195 pub fn get_reader(&self) -> Arc<AsyncDeviceStateReader> {
197 Arc::new(AsyncDeviceStateReader {
198 device: self.clone(),
199 states: Mutex::new(DeviceState {
200 buttons: vec![false; self.kind.key_count() as usize + self.kind.touchpoint_count() as usize],
201 encoders: vec![false; self.kind.encoder_count() as usize],
202 }),
203 })
204 }
205}
206
207pub struct AsyncDeviceStateReader {
209 device: AsyncStreamDeck,
210 states: Mutex<DeviceState>,
211}
212
213impl AsyncDeviceStateReader {
214 pub async fn read(&self, poll_rate: f32) -> Result<Vec<DeviceStateUpdate>, StreamDeckError> {
216 let input = self.device.read_input(poll_rate).await?;
217 let mut my_states = self.states.lock().await;
218
219 let mut updates = vec![];
220
221 match input {
222 StreamDeckInput::ButtonStateChange(buttons) => {
223 for (index, (their, mine)) in zip(buttons.iter(), my_states.buttons.iter()).enumerate() {
224 if self.device.kind.is_mirabox() && self.device.kind != Kind::MiraBoxN3EN {
225 if *their {
226 updates.push(DeviceStateUpdate::ButtonDown(index as u8));
227 updates.push(DeviceStateUpdate::ButtonUp(index as u8));
228 }
229 } else if *their != *mine {
230 if index < self.device.kind.key_count() as usize {
231 if *their {
232 updates.push(DeviceStateUpdate::ButtonDown(index as u8));
233 } else {
234 updates.push(DeviceStateUpdate::ButtonUp(index as u8));
235 }
236 } else if *their {
237 updates.push(DeviceStateUpdate::TouchPointDown(index as u8 - self.device.kind.key_count()));
238 } else {
239 updates.push(DeviceStateUpdate::TouchPointUp(index as u8 - self.device.kind.key_count()));
240 }
241 }
242 }
243
244 my_states.buttons = buttons;
245 }
246
247 StreamDeckInput::EncoderStateChange(encoders) => {
248 for (index, (their, mine)) in zip(encoders.iter(), my_states.encoders.iter()).enumerate() {
249 if self.device.kind.is_mirabox() && self.device.kind != Kind::MiraBoxN3EN {
250 if *their {
251 updates.push(DeviceStateUpdate::EncoderDown(index as u8));
252 updates.push(DeviceStateUpdate::EncoderUp(index as u8));
253 }
254 } else if *their != *mine {
255 if *their {
256 updates.push(DeviceStateUpdate::EncoderDown(index as u8));
257 } else {
258 updates.push(DeviceStateUpdate::EncoderUp(index as u8));
259 }
260 }
261 }
262
263 my_states.encoders = encoders;
264 }
265
266 StreamDeckInput::EncoderTwist(twist) => {
267 for (index, change) in twist.iter().enumerate() {
268 if *change != 0 {
269 updates.push(DeviceStateUpdate::EncoderTwist(index as u8, *change));
270 }
271 }
272 }
273
274 StreamDeckInput::TouchScreenPress(x, y) => {
275 updates.push(DeviceStateUpdate::TouchScreenPress(x, y));
276 }
277
278 StreamDeckInput::TouchScreenLongPress(x, y) => {
279 updates.push(DeviceStateUpdate::TouchScreenLongPress(x, y));
280 }
281
282 StreamDeckInput::TouchScreenSwipe(s, e) => {
283 updates.push(DeviceStateUpdate::TouchScreenSwipe(s, e));
284 }
285
286 _ => {}
287 }
288
289 drop(my_states);
290
291 Ok(updates)
292 }
293}