streamduck_core/core/
mod.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use streamdeck::{Kind, StreamDeck};
7use tokio::sync::{Mutex, RwLock};
8use tokio::sync::mpsc::unbounded_channel;
9
10pub use methods::check_feature_list_for_feature;
11pub use methods::CoreHandle;
12pub use methods::warn_for_feature;
13
14use crate::ImageCollection;
15use crate::config::{Config, UniqueDeviceConfig};
16use crate::core::button::Button;
17use crate::modules::events::SDGlobalEvent;
18use crate::modules::ModuleManager;
19use crate::socket::SocketManager;
20use crate::thread::{DeviceThreadCommunication, DeviceThreadHandle, spawn_device_thread};
21use crate::thread::rendering::custom::RenderingManager;
22
23/// Definitions of button structs
24pub mod button;
25
26/// Methods for interacting with the core
27mod methods;
28pub mod manager;
29
30/// Reference counted RwLock of a button, prevents data duplication and lets you edit buttons if they're in many stacks at once
31pub type UniqueButton = Arc<RwLock<Button>>;
32
33/// Map of UniqueButtons
34pub type UniqueButtonMap = HashMap<u8, UniqueButton>;
35
36/// Map of Buttons
37pub type ButtonMap = HashMap<u8, Button>;
38
39/// Hashmap of UniqueButtons
40pub type ButtonPanel = Arc<RwLock<Panel<UniqueButtonMap>>>;
41
42/// Hashmap of raw Buttons
43pub type RawButtonPanel = Panel<ButtonMap>;
44
45/// Panel definition
46#[derive(Serialize, Deserialize, Clone, Default, Debug)]
47pub struct Panel<T> {
48    /// Display name that will be shown in UI
49    #[serde(default)]
50    pub display_name: String,
51    /// Data to keep with stack
52    #[serde(default)]
53    pub data: Value,
54    /// Buttons of the panel
55    #[serde(default)]
56    pub buttons: T
57}
58
59impl Into<ButtonMap> for Panel<ButtonMap> {
60    fn into(self) -> ButtonMap {
61        self.buttons
62    }
63}
64
65impl Into<ButtonMap> for &Panel<ButtonMap> {
66    fn into(self) -> ButtonMap {
67        self.buttons.clone()
68    }
69}
70
71impl Into<UniqueButtonMap> for Panel<UniqueButtonMap> {
72    fn into(self) -> UniqueButtonMap {
73        self.buttons
74    }
75}
76
77impl Into<UniqueButtonMap> for &Panel<UniqueButtonMap> {
78    fn into(self) -> UniqueButtonMap {
79        self.buttons.clone()
80    }
81}
82
83/// Core struct that contains all relevant information about streamdeck and manages the streamdeck
84#[allow(dead_code)]
85pub struct SDCore {
86    /// Serial number of the device
87    pub serial_number: String,
88
89    /// Module manager
90    pub module_manager: Arc<ModuleManager>,
91
92    /// Rendering manager
93    pub render_manager: Arc<RenderingManager>,
94
95    /// Socket manager
96    pub socket_manager: Arc<SocketManager>,
97
98    /// Config
99    pub config: Arc<Config>,
100
101    /// Device config associated with the device
102    pub device_config: UniqueDeviceConfig,
103
104    /// Current panel stack
105    pub current_stack: Mutex<Vec<ButtonPanel>>,
106
107    /// Image size supported by streamdeck
108    pub image_size: (usize, usize),
109
110    /// Image collection to use for thread
111    pub image_collection: ImageCollection,
112
113    /// Kind of streamdeck device
114    pub kind: Kind,
115
116    /// Key count of the streamdeck device
117    pub key_count: u8,
118
119    /// Pool rate of how often should the core read events from the device
120    pub frame_rate: u32,
121
122    /// Decides if core is dead
123    pub should_close: RwLock<bool>,
124
125    handles: Mutex<Option<ThreadHandles>>
126}
127
128impl SDCore {
129    /// Creates an instance of core that is already dead
130    pub async fn blank(module_manager: Arc<ModuleManager>, render_manager: Arc<RenderingManager>, socket_manager: Arc<SocketManager>, config: Arc<Config>, device_config: UniqueDeviceConfig, image_collection: ImageCollection) -> Arc<SDCore> {
131        let serial_number = device_config.read().await.serial.to_string();
132        Arc::new(SDCore {
133            serial_number,
134            module_manager,
135            render_manager,
136            socket_manager,
137            config,
138            device_config,
139            current_stack: Mutex::new(vec![]),
140            handles: Mutex::new(None),
141            image_size: (0, 0),
142            image_collection,
143            kind: Kind::Original,
144            key_count: 0,
145            frame_rate: 0,
146            should_close: RwLock::new(true)
147        })
148    }
149
150    /// Creates an instance of the core over existing streamdeck connection
151    pub async fn new(module_manager: Arc<ModuleManager>, render_manager: Arc<RenderingManager>, socket_manager: Arc<SocketManager>, config: Arc<Config>, device_config: UniqueDeviceConfig, image_collection: ImageCollection, mut connection: StreamDeck, frame_rate: u32) -> Arc<SDCore> {
152        let (key_tx, mut key_rx) = unbounded_channel();
153
154        let serial_number = device_config.read().await.serial.to_string();
155        let serial_number = connection.serial().unwrap_or_else(|_| serial_number);
156
157        module_manager.send_global_event_to_modules(SDGlobalEvent::DeviceConnected {
158            serial_number: serial_number.clone()
159        }).await;
160
161        let core = Arc::new(SDCore {
162            serial_number,
163            module_manager,
164            render_manager,
165            socket_manager,
166            config,
167            device_config,
168            current_stack: Mutex::new(vec![]),
169            handles: Mutex::new(None),
170            image_size: connection.image_size(),
171            image_collection,
172            kind: connection.kind(),
173            key_count: connection.kind().keys(),
174            frame_rate,
175            should_close: RwLock::new(false)
176        });
177
178        let renderer = spawn_device_thread(core.clone(), connection, key_tx);
179
180        *core.handles.lock().await = Some(
181            ThreadHandles {
182                renderer
183            }
184        );
185
186        let task_core = CoreHandle::wrap(core.clone());
187        tokio::spawn(async move {
188            loop {
189                if task_core.core().is_closed().await {
190                    break
191                }
192
193                if let Some((key, state)) = key_rx.recv().await {
194                    if state {
195                        task_core.button_down(key).await;
196                    } else {
197                        task_core.button_up(key).await;
198                    }
199                } else {
200                    break;
201                }
202            }
203        });
204
205        core
206    }
207
208    /// Tells device thread to refresh screen
209    pub async fn mark_for_redraw(&self) {
210        let handles = self.handles.lock().await;
211
212        handles.as_ref().unwrap().renderer.send(vec![DeviceThreadCommunication::RefreshScreen]);
213    }
214
215    /// Sends commands to streamdeck thread
216    pub async fn send_commands(&self, commands: Vec<DeviceThreadCommunication>) {
217        let handles = self.handles.lock().await;
218
219        handles.as_ref().unwrap().renderer.send(commands);
220    }
221
222    /// Gets serial number of the core
223    pub async fn serial_number(&self) -> String {
224        self.device_config.read().await.serial.to_string()
225    }
226
227    /// Checks if core is supposed to be closed
228    pub async fn is_closed(&self) -> bool {
229        *self.should_close.read().await
230    }
231
232    /// Kills the core and all the related threads
233    pub async fn close(&self) {
234        self.module_manager.send_global_event_to_modules(SDGlobalEvent::DeviceDisconnected {
235            serial_number: self.serial_number.to_string()
236        }).await;
237
238        let mut lock = self.should_close.write().await;
239        *lock = true;
240    }
241}
242
243struct ThreadHandles {
244    pub renderer: DeviceThreadHandle
245}