streamduck_core/core/
methods.rs

1use std::collections::HashMap;
2use std::ops::DerefMut;
3use std::sync::Arc;
4use std::time::Instant;
5
6use image::{DynamicImage, Rgba};
7use serde::de::Error as DeError;
8use serde_json::{Map, Value};
9use serde_json::Error as JSONError;
10use tokio::sync::MutexGuard;
11
12use crate::{Config, ModuleManager, SDCore, SocketManager};
13use crate::core::{ButtonPanel, UniqueButton};
14use crate::core::button::{Button, parse_unique_button_to_component};
15use crate::modules::{features_to_vec, UniqueSDModule};
16use crate::modules::components::{UIPathValue, UIValue};
17use crate::modules::core_module::CoreSettings;
18use crate::modules::events::SDCoreEvent;
19use crate::thread::DeviceThreadCommunication;
20use crate::thread::rendering::{draw_background, draw_custom_renderer_texture, draw_foreground, draw_missing_texture, RendererComponent};
21use crate::thread::util::image_from_solid;
22use crate::util::{add_array_function, button_to_raw, change_from_path, convert_value_to_path, deserialize_panel, make_button_unique, panel_to_raw, remove_array_function, serialize_panel, set_value_function};
23use crate::versions::SUPPORTED_FEATURES;
24
25/// Handle that's given out to a module to perform actions on the core
26#[derive(Clone)]
27pub struct CoreHandle {
28    pub(crate) core: Arc<SDCore>,
29    pub(crate) module_name: String,
30    pub(crate) module_features: Vec<(String, String)>,
31}
32
33/// Checks if slice of features contains a specific feature
34pub fn check_feature_list_for_feature(features: &Vec<(String, String)>, feature: &str) -> bool {
35    for (feat, _) in features {
36        if *feat == feature {
37            return true;
38        }
39    }
40
41    false
42}
43
44/// Warns if slice of features doesn't contain a specific feature
45pub fn warn_for_feature(module_name: &str, features: &Vec<(String, String)>, feature: &str) {
46    if !check_feature_list_for_feature(features, feature) {
47        log::warn!("Module '{}' is using unreported feature '{}', please add the feature into plugin metadata to prevent any future crashes due to version incompatibility", module_name, feature);
48    }
49}
50
51impl CoreHandle {
52    /// Wraps core reference with a handle, used for all core features to be able to bypass feature checking
53    pub fn wrap(core: Arc<SDCore>) -> CoreHandle {
54        CoreHandle {
55            core,
56            module_name: "-system-".to_string(),
57            module_features: features_to_vec(SUPPORTED_FEATURES)
58        }
59    }
60
61    /// Checks if module is allowed to use this feature
62    pub fn check_for_feature(&self, feature: &str) -> bool {
63        check_feature_list_for_feature(&self.module_features, feature)
64    }
65
66    /// Warns if module is using feature it hasn't reported
67    pub fn required_feature(&self, feature: &str) {
68        warn_for_feature(&self.module_name, &self.module_features, feature)
69    }
70
71    /// Clones the handle for specified module
72    pub fn clone_for(&self, module: &UniqueSDModule) -> CoreHandle {
73        CoreHandle {
74            core: self.core.clone(),
75            module_name: module.name(),
76            module_features: module.metadata().used_features
77        }
78    }
79
80    /// Returns core reference
81    pub fn core(&self) -> Arc<SDCore> {
82        self.required_feature("core");
83        self.core.clone()
84    }
85
86    /// Returns config reference
87    pub fn config(&self) -> Arc<Config> {
88        self.required_feature("config");
89        self.core.config.clone()
90    }
91
92    /// Returns module manager reference
93    pub fn module_manager(&self) -> Arc<ModuleManager> {
94        self.required_feature("module_manager");
95        self.core.module_manager.clone()
96    }
97
98    /// Returns socket manager reference
99    pub fn socket_manager(&self) -> Arc<SocketManager> {
100        self.required_feature("socket_api");
101        self.core.socket_manager.clone()
102    }
103
104    /// Returns current stack lock
105    pub async fn current_stack(&self) -> MutexGuard<'_, Vec<ButtonPanel>> {
106        self.required_feature("core");
107        self.core.current_stack.lock().await
108    }
109
110    /// Sends core event to all modules, spawns a separate thread to do it, so doesn't block current thread
111    pub async fn send_core_event_to_modules<T: Iterator<Item=UniqueSDModule> + Send + 'static>(&self, event: SDCoreEvent, modules: T) {
112        let core = self.clone();
113        for module in modules {
114            if module.name() == core.module_name {
115                continue;
116            }
117
118            let task_core = core.clone_for(&module);
119            let task_event = event.clone();
120            tokio::spawn(async move {
121                module.event(task_core, task_event).await;
122            });
123        }
124    }
125
126    /// Gets current panel stack
127    pub async fn get_stack(&self) -> Vec<ButtonPanel> {
128        self.required_feature("core_methods");
129        let stack = self.current_stack().await;
130
131        stack.iter().map(|x| x.clone()).collect()
132    }
133
134    /// Gets panel that's currently on top of the stack
135    pub async fn get_current_screen(&self) -> Option<ButtonPanel> {
136        self.required_feature("core_methods");
137        let stack = self.current_stack().await;
138
139        if let Some(screen) = stack.last() {
140            Some(screen.clone())
141        } else {
142            None
143        }
144    }
145
146    /// Returns a button from current screen on specified position
147    pub async fn get_button(&self, key: u8) -> Option<UniqueButton> {
148        self.required_feature("core_methods");
149        if let Some(screen) = self.get_current_screen().await {
150            let handle = screen.read().await;
151            handle.buttons.get(&key).cloned()
152        } else {
153            None
154        }
155    }
156
157    /// Sets button to current screen with specified position
158    pub async fn set_button(&self, key: u8, button: UniqueButton) -> bool {
159        self.required_feature("core_methods");
160        if let Some(screen) = self.get_current_screen().await {
161            let mut handle = screen.write().await;
162            let previous_button = handle.buttons.get(&key).cloned();
163
164            handle.buttons.insert(key, button.clone());
165
166            drop(handle);
167
168            if let Some(previous_button) = previous_button {
169                self.send_core_event_to_modules(SDCoreEvent::ButtonUpdated {
170                    key,
171                    panel: screen.clone(),
172                    new_button: button.clone(),
173                    old_button: previous_button.clone()
174                }, self.module_manager().get_module_list().await.into_iter()).await;
175            } else {
176                self.send_core_event_to_modules( SDCoreEvent::ButtonAdded {
177                    key,
178                    panel: screen.clone(),
179                    added_button: button.clone()
180                }, self.module_manager().get_module_list().await.into_iter()).await;
181            }
182
183            self.core.mark_for_redraw().await;
184
185            true
186        } else {
187            false
188        }
189    }
190
191    /// Clears button from current screen on specified position
192    pub async fn clear_button(&self, key: u8) -> bool {
193        self.required_feature("core_methods");
194        if let Some(screen) = self.get_current_screen().await {
195            let mut handle = screen.write().await;
196            if let Some(button) = handle.buttons.remove(&key) {
197                drop(handle);
198
199                self.send_core_event_to_modules( SDCoreEvent::ButtonDeleted {
200                    key,
201                    panel: screen.clone(),
202                    deleted_button: button.clone()
203                }, self.module_manager().get_module_list().await.into_iter()).await;
204
205                self.core.mark_for_redraw().await;
206
207                true
208            } else {
209                false
210            }
211        } else {
212            false
213        }
214    }
215
216    /// Adds component onto a button, returns success boolean
217    pub async fn add_component(&self, key: u8, component_name: &str) -> bool {
218        self.required_feature("core_methods");
219
220        let module_manager = self.module_manager();
221
222        if let Some(screen) = self.get_current_screen().await {
223            let handle = screen.read().await;
224            if let Some(button) = handle.buttons.get(&key).cloned() {
225                let previous = make_button_unique(button_to_raw(&button).await);
226
227                let mut button_handle = button.write().await;
228                drop(handle);
229
230                if !button_handle.component_names().contains(&component_name.to_string()) {
231                    let components = module_manager.read_component_map().await;
232
233                    if let Some((_, module)) = components.get(component_name) {
234                        module.add_component(self.clone_for(&module), button_handle.deref_mut(), component_name).await;
235
236                        drop(button_handle);
237                        drop(components);
238
239                        self.send_core_event_to_modules(SDCoreEvent::ButtonUpdated {
240                            key,
241                            panel: screen.clone(),
242                            new_button: button.clone(),
243                            old_button: previous.clone()
244                        }, self.module_manager().get_module_list().await.into_iter()).await;
245
246                        self.core.mark_for_redraw().await;
247
248                        return true;
249                    }
250                }
251            }
252        }
253
254        false
255    }
256
257    /// Gets component values from a component on a button
258    pub async fn get_component_values(&self, key: u8, component_name: &str) -> Option<Vec<UIValue>> {
259        self.required_feature("core_methods");
260
261        let module_manager = self.module_manager();
262
263        if let Some(screen) = self.get_current_screen().await {
264            let handle = screen.read().await;
265            if let Some(button) = handle.buttons.get(&key).cloned() {
266                let mut button_handle = button.write().await;
267                drop(handle);
268
269                if button_handle.component_names().contains(&component_name.to_string()) {
270                    let components = module_manager.read_component_map().await;
271
272                    if let Some((_, module)) = components.get(component_name) {
273                        return Some(module.component_values(self.clone_for(&module), button_handle.deref_mut(), component_name).await);
274                    }
275                }
276            }
277        }
278
279        None
280    }
281
282    /// Gets component values from component on a button, but with paths for easier interaction with values
283    pub async fn get_component_values_with_paths(&self, key: u8, component_name: &str) -> Option<Vec<UIPathValue>> {
284        self.required_feature("core_methods");
285
286        if let Some(values) = self.get_component_values(key, component_name).await {
287            Some(values.into_iter().map(|x| convert_value_to_path(x, "")).collect())
288        } else {
289            None
290        }
291    }
292
293    /// Sets component values based on changes for component on a button
294    pub async fn set_component_value(&self, key: u8, component_name: &str, value: Vec<UIValue>) -> bool {
295        self.required_feature("core_methods");
296
297        let module_manager = self.module_manager();
298
299        if let Some(screen) = self.get_current_screen().await {
300            let handle = screen.read().await;
301            if let Some(button) = handle.buttons.get(&key).cloned() {
302                let previous = make_button_unique(button_to_raw(&button).await);
303
304                let mut button_handle = button.write().await;
305                drop(handle);
306
307                if button_handle.component_names().contains(&component_name.to_string()) {
308                    let components = module_manager.read_component_map().await;
309
310                    if let Some((_, module)) = components.get(component_name) {
311                        module.set_component_value(self.clone_for(&module), button_handle.deref_mut(), component_name, value).await;
312                        drop(button_handle);
313                        drop(components);
314
315                        self.send_core_event_to_modules(SDCoreEvent::ButtonUpdated {
316                            key,
317                            panel: screen.clone(),
318                            new_button: button.clone(),
319                            old_button: previous.clone()
320                        }, self.module_manager().get_module_list().await.into_iter()).await;
321
322                        self.core.mark_for_redraw().await;
323
324                        return true;
325                    }
326                }
327            }
328        }
329
330        false
331    }
332
333    /// Adds new array element to a component value
334    pub async fn add_element_component_value(&self, key: u8, component_name: &str, path: &str) -> bool {
335        self.required_feature("core_methods");
336
337        if let Some(values) = self.get_component_values(key, component_name).await {
338            let (changes, success) = change_from_path(path, values, &add_array_function(), false);
339
340            if success {
341                if !changes.is_empty() {
342                    self.set_component_value(key, component_name, changes).await
343                } else {
344                    false
345                }
346            } else {
347                false
348            }
349        } else {
350            false
351        }
352    }
353
354    /// Removes element from array in component value
355    pub async fn remove_element_component_value(&self, key: u8, component_name: &str, path: &str, index: usize) -> bool {
356        self.required_feature("core_methods");
357
358        if let Some(values) = self.get_component_values(key, component_name).await {
359            let (changes, success) = change_from_path(path, values, &remove_array_function(index), false);
360
361            if success {
362                if !changes.is_empty() {
363                    self.set_component_value(key, component_name, changes).await
364                } else {
365                    false
366                }
367            } else {
368                false
369            }
370        } else {
371            false
372        }
373    }
374
375    /// Sets value based on path for component value
376    pub async fn set_component_value_by_path(&self, key: u8, component_name: &str, value: UIPathValue) -> bool {
377        self.required_feature("core_methods");
378
379        if let Some(values) = self.get_component_values(key, component_name).await {
380            let (changes, success) = change_from_path(&value.path, values, &set_value_function(value.clone()), false);
381
382            if success {
383                if !changes.is_empty() {
384                    self.set_component_value(key, component_name, changes).await
385                } else {
386                    false
387                }
388            } else {
389                false
390            }
391        } else {
392            false
393        }
394    }
395
396    /// Removes component from a button
397    pub async fn remove_component(&self, key: u8, component_name: &str) -> bool {
398        self.required_feature("core_methods");
399
400        let module_manager = self.module_manager();
401
402        if let Some(screen) = self.get_current_screen().await {
403            let handle = screen.read().await;
404            if let Some(button) = handle.buttons.get(&key).cloned() {
405                let previous = make_button_unique(button_to_raw(&button).await);
406
407                let mut button_handle = button.write().await;
408                drop(handle);
409
410                if button_handle.component_names().contains(&component_name.to_string()) {
411                    let components = module_manager.read_component_map().await;
412
413                    if let Some((_, module)) = components.get(component_name) {
414                        module.remove_component(self.clone_for(&module), button_handle.deref_mut(), component_name).await;
415
416                        drop(button_handle);
417                        drop(components);
418
419                        self.send_core_event_to_modules(SDCoreEvent::ButtonUpdated {
420                            key,
421                            panel: screen.clone(),
422                            new_button: button.clone(),
423                            old_button: previous.clone()
424                        }, self.module_manager().get_module_list().await.into_iter()).await;
425
426                        self.core.mark_for_redraw().await;
427
428                        return true;
429                    }
430                }
431            }
432        }
433
434        false
435    }
436
437    /// Creates a new button taking provided one as an example and makes all responsible modules handle the paste action
438    pub async fn paste_button(&self, key: u8, reference_button: &Button) -> bool {
439        let mut new_button = Button::new();
440
441        let responsible_modules = self.module_manager().get_modules_for_declared_components(reference_button.component_names().as_slice()).await;
442        for module in responsible_modules {
443            module.paste_component(self.clone_for(&module), reference_button, &mut new_button).await;
444        }
445
446        println!("resulting button: {:?}", new_button);
447
448        self.set_button(key, make_button_unique(new_button)).await
449    }
450
451    /// Pushes new panel into the stack
452    pub async fn push_screen(&self, screen: ButtonPanel) {
453        self.required_feature("core_methods");
454        let mut stack = self.current_stack().await;
455
456        stack.push(screen.clone());
457        drop(stack);
458
459        self.send_core_event_to_modules(SDCoreEvent::PanelPushed {
460            new_panel: screen.clone()
461        }, self.module_manager().get_module_list().await.into_iter()).await;
462
463        self.core.mark_for_redraw().await;
464    }
465
466    /// Pops panel from stack
467    pub async fn pop_screen(&self) {
468        self.required_feature("core_methods");
469        let mut stack = self.current_stack().await;
470
471        let old_panel = stack.pop();
472        drop(stack);
473
474        if let Some(old_panel) = old_panel {
475            self.send_core_event_to_modules(SDCoreEvent::PanelPopped {
476                popped_panel: old_panel.clone()
477            }, self.module_manager().get_module_list().await.into_iter()).await;
478        }
479
480        self.core.mark_for_redraw().await;
481    }
482
483    /// Returns first panel of the stack for saving purposes
484    pub async fn get_root_screen(&self) -> ButtonPanel {
485        self.required_feature("core_methods");
486        let stack = self.current_stack().await;
487        stack.get(0).unwrap().clone()
488    }
489
490    /// Returns first panel of the stack that's already been serialized
491    pub async fn save_panels_to_value(&self) -> Value {
492        self.required_feature("core_methods");
493        let stack = self.current_stack().await;
494
495        if let Some(panel) = stack.get(0) {
496            let serialized_panel = serialize_panel(panel.clone()).await.unwrap();
497            serde_json::to_value(&serialized_panel).unwrap()
498        } else {
499            Value::Object(Map::new())
500        }
501    }
502
503    /// Clears the stack and loads provided panel into the stack
504    pub async fn reset_stack(&self, panel: ButtonPanel) {
505        self.required_feature("core_methods");
506        let mut stack = self.current_stack().await;
507
508        stack.clear();
509        stack.push(panel.clone());
510        drop(stack);
511
512        self.send_core_event_to_modules(SDCoreEvent::StackReset {
513            new_panel: panel.clone()
514        }, self.module_manager().get_module_list().await.into_iter()).await;
515
516        self.core.mark_for_redraw().await;
517    }
518
519    /// Clears the stack, attempts to deserialize provided panel value into an actual panel and then pushes it into the stack
520    pub async fn load_panels_from_value(&self, panels: Value) -> Result<(), JSONError> {
521        self.required_feature("core_methods");
522        match deserialize_panel(panels) {
523            Ok(panel) => {
524                let mut stack = self.current_stack().await;
525
526                stack.clear();
527                stack.push(panel.clone());
528                drop(stack);
529
530                self.send_core_event_to_modules(SDCoreEvent::StackReset {
531                    new_panel: panel.clone()
532                }, self.module_manager().get_module_list().await.into_iter()).await;
533
534                self.core.mark_for_redraw().await;
535
536                Ok(())
537            }
538            Err(err) => {
539                Err(DeError::custom(format!("Failed to load panels: {}", err)))
540            }
541        }
542    }
543
544    /// Triggers button down event on all modules
545    pub async fn button_down(&self, key: u8) {
546        self.required_feature("core_methods");
547        self.send_core_event_to_modules(SDCoreEvent::ButtonDown {
548            key
549        }, self.module_manager().get_module_list().await.into_iter()).await;
550    }
551
552    /// Triggers button up event on all modules
553    pub async fn button_up(&self, key: u8) {
554        self.required_feature("core_methods");
555        self.send_core_event_to_modules(SDCoreEvent::ButtonUp {
556            key
557        }, self.module_manager().get_module_list().await.into_iter()).await;
558
559        self.button_action(key).await;
560    }
561
562    /// Triggers button action event for modules that are related to components of the button
563    pub async fn button_action(&self, key: u8) {
564        self.required_feature("core_methods");
565        if let Some(screen) = self.get_current_screen().await {
566            let handle = screen.read().await;
567            if let Some(button) = handle.buttons.get(&key).cloned() {
568                drop(handle);
569
570                let event = SDCoreEvent::ButtonAction {
571                    key,
572                    panel: screen.clone(),
573                    pressed_button: button.clone()
574                };
575
576                self.send_core_event_to_modules(
577                    event.clone(),
578                    self.module_manager()
579                        .get_modules_for_components(button.read().await.component_names().as_slice()).await
580                        .into_iter()
581                ).await;
582                //send_event_to_socket(&self.core.socket_manager, core_event_to_global(event, &self.core.serial_number).await).await;
583
584                self.core.mark_for_redraw().await;
585            }
586        }
587    }
588
589    /// Renders what current screen would look like into [DynamicImage] map
590    pub async fn get_button_images(&self) -> Option<HashMap<u8, DynamicImage>> {
591        let missing = draw_missing_texture(self.core.image_size);
592        let custom = draw_custom_renderer_texture(self.core.image_size);
593        let blank = image_from_solid(self.core.image_size, Rgba([0, 0, 0, 255]));
594
595        let panel = self.get_current_screen().await?;
596        let current_screen = panel.read().await;
597        let buttons = current_screen.buttons.clone();
598
599        let renderers = self.core.render_manager.read_renderers().await;
600
601        let core_settings: CoreSettings = self.core.config.get_plugin_settings().await.unwrap_or_default();
602
603
604        let mut images = HashMap::new();
605
606        for (key, button) in buttons {
607            if let Ok(component) = parse_unique_button_to_component::<RendererComponent>(&button).await {
608                let modules = self.module_manager()
609                    .get_modules_for_rendering(
610                        &button.read().await.component_names()
611                    ).await;
612                let modules = modules.into_values()
613                    .filter(|x| !component.plugin_blacklist.contains(&x.name()))
614                    .filter(|x| !core_settings.renderer.plugin_blacklist.contains(&x.name()))
615                    .collect::<Vec<UniqueSDModule>>();
616
617                let image = if component.renderer.is_empty() {
618                    draw_foreground(
619                        &component,
620                        &button,
621                        &modules,
622                        draw_background(
623                            &component,
624                            self,
625                            &missing
626                        ).await,
627                        self
628                    ).await
629                } else {
630                    if let Some(renderer) = renderers.get(&component.renderer) {
631                        if let Some(image) = renderer.representation(key, &button, self).await {
632                            image
633                        } else {
634                            custom.clone()
635                        }
636                    } else {
637                        custom.clone()
638                    }
639                };
640
641                images.insert(key, image);
642            } else {
643                images.insert(key, blank.clone());
644            }
645        }
646
647
648        Some(images)
649    }
650
651    /// Renders what specified button would look like into [DynamicImage]
652    pub async fn get_button_image(&self, key: u8) -> Option<DynamicImage> {
653        let missing = draw_missing_texture(self.core.image_size);
654        let custom = draw_custom_renderer_texture(self.core.image_size);
655        let blank = image_from_solid(self.core.image_size, Rgba([0, 0, 0, 255]));
656
657        let button = self.get_button(key).await?;
658        let renderers = self.core.render_manager.read_renderers().await;
659
660        let core_settings: CoreSettings = self.core.config.get_plugin_settings().await.unwrap_or_default();
661
662        if let Ok(component) = parse_unique_button_to_component::<RendererComponent>(&button).await {
663            let modules = self.module_manager().get_modules_for_rendering(&button.read().await.component_names()).await;
664            let modules = modules.into_values()
665                .filter(|x| !component.plugin_blacklist.contains(&x.name()))
666                .filter(|x| !core_settings.renderer.plugin_blacklist.contains(&x.name()))
667                .collect::<Vec<UniqueSDModule>>();
668
669            let image = if component.renderer.is_empty() {
670                draw_foreground(
671                    &component,
672                    &button,
673                    &modules,
674                    draw_background(
675                        &component,
676                        self,
677                        &missing
678                    ).await,
679                    self
680                ).await
681            } else {
682                if let Some(renderer) = renderers.get(&component.renderer) {
683                    if let Some(image) = renderer.representation(key, &button, self).await {
684                        image
685                    } else {
686                        custom.clone()
687                    }
688                } else {
689                    custom.clone()
690                }
691            };
692
693            Some(image)
694        } else {
695            Some(blank)
696        }
697    }
698
699    /// Replaces current screen with specified one
700    pub async fn replace_screen(&self, screen: ButtonPanel) {
701        self.required_feature("core_methods");
702        let mut stack = self.current_stack().await;
703
704        let old_panel = stack.pop();
705        stack.push(screen.clone());
706
707        self.send_core_event_to_modules(SDCoreEvent::PanelReplaced {
708            old_panel,
709            new_panel: screen
710        }, self.module_manager().get_module_list().await.into_iter()).await;
711
712        self.core.mark_for_redraw().await;
713    }
714
715    /// Sets brightness of the streamdeck to specified (Range from 0 to 100)
716    pub async fn set_brightness(&self, brightness: u8) {
717        self.required_feature("core_methods");
718        self.core.send_commands(vec![DeviceThreadCommunication::SetBrightness(brightness)]).await;
719
720        let mut handle = self.core.device_config.write().await;
721        handle.brightness = brightness;
722    }
723
724    /// Commits all changes to layout to device config so it can be later saved
725    pub async fn commit_changes(&self) {
726        self.required_feature("core_methods");
727        let stack = self.get_root_screen().await;
728
729        let mut handle = self.core.device_config.write().await;
730        handle.layout = panel_to_raw(&stack).await;
731
732        handle.dirty_state = true;
733        handle.commit_time = Some(Instant::now());
734        log::debug!("new commit to {}", handle.serial);
735    }
736}