streamdeck_oxide/
run.rs

1use std::{marker::PhantomData, sync::Arc};
2
3use elgato_streamdeck::AsyncStreamDeck;
4
5use crate::{
6    button::RenderConfig, navigation::NavigationEntry, theme::Theme, view::DisplayManager,
7};
8
9/// Run a Stream Deck application with the specified configuration.
10///
11/// This function takes a theme, render configuration, Stream Deck instance,
12/// and application context, and runs the main event loop.
13pub async fn run<N, W, H, C>(
14    theme: Theme,
15    config: RenderConfig,
16    deck: Arc<AsyncStreamDeck>,
17    context: C,
18) -> Result<(), Box<dyn std::error::Error>>
19where
20    W: generic_array::ArrayLength,
21    H: generic_array::ArrayLength,
22    C: Send + Sync + Clone + 'static,
23    N: NavigationEntry<W, H, C>,
24{
25    let (display_manager, mut navigation_receiver) =
26        DisplayManager::<N, W, H, C>::new(deck.clone(), config, theme, context).await?;
27
28    display_manager.fetch_all().await?;
29    display_manager.render().await?;
30
31    let reader = deck.get_reader();
32    loop {
33        let events_future = reader.read(10.0);
34        let navigation_future = navigation_receiver.recv();
35        tokio::select! {
36            events = events_future => {
37                let events = events?;
38                for event in events {
39                    match event {
40                        elgato_streamdeck::DeviceStateUpdate::ButtonDown(id) => {
41                            display_manager.on_press(id).await?;
42                        }
43                        elgato_streamdeck::DeviceStateUpdate::ButtonUp(id) => {
44                            display_manager.on_release(id).await?;
45                        }
46                        _ => {}
47                    }
48                }
49            }
50            Some(navigation) = navigation_future => {
51                display_manager.navigate_to(navigation).await?;
52                display_manager.fetch_all().await?;
53                display_manager.render().await?;
54            }
55        }
56    }
57}
58
59pub struct ExternalTrigger<N, W, H, C> {
60    /// The navigation entry.
61    pub(crate) navigation: N,
62    /// Whether to force switching views.
63    pub(crate) switch_view: bool,
64    pub(crate) _marker: PhantomData<(W, H, C)>,
65}
66
67impl<N, W, H, C> ExternalTrigger<N, W, H, C> {
68    /// Create a new external trigger.
69    pub fn new(navigation: N, switch_view: bool) -> Self {
70        Self {
71            navigation,
72            switch_view,
73            _marker: PhantomData,
74        }
75    }
76}
77
78/// Run a Stream Deck application with the specified configuration and external triggers.
79///
80/// This function takes a theme, render configuration, Stream Deck instance,
81/// aplication context, and external triggers, and runs the main event loop.
82pub async fn run_with_external_triggers<N, W, H, C>(
83    theme: Theme,
84    config: RenderConfig,
85    deck: Arc<AsyncStreamDeck>,
86    context: C,
87    mut receiver: tokio::sync::mpsc::Receiver<ExternalTrigger<N, W, H, C>>,
88) -> Result<(), Box<dyn std::error::Error>>
89where
90    W: generic_array::ArrayLength,
91    H: generic_array::ArrayLength,
92    C: Send + Sync + Clone + 'static,
93    N: NavigationEntry<W, H, C>,
94{
95    let (display_manager, mut navigation_receiver) =
96        DisplayManager::<N, W, H, C>::new(deck.clone(), config, theme, context).await?;
97
98    display_manager.fetch_all().await?;
99    display_manager.render().await?;
100
101    let reader = deck.get_reader();
102    loop {
103        let events_future = reader.read(10.0);
104        let navigation_future = navigation_receiver.recv();
105        let trigger_future = receiver.recv();
106        tokio::select! {
107            events = events_future => {
108                let events = events?;
109                for event in events {
110                    match event {
111                        elgato_streamdeck::DeviceStateUpdate::ButtonDown(id) => {
112                            display_manager.on_press(id).await?;
113                        }
114                        elgato_streamdeck::DeviceStateUpdate::ButtonUp(id) => {
115                            display_manager.on_release(id).await?;
116                        }
117                        _ => {}
118                    }
119                }
120            }
121            Some(navigation) = navigation_future => {
122                display_manager.navigate_to(navigation).await?;
123                display_manager.fetch_all().await?;
124                display_manager.render().await?;
125            }
126            Some(trigger) = trigger_future => {
127                if trigger.switch_view || trigger.navigation == display_manager.get_current_navigation().await? {
128                    display_manager.navigate_to(trigger.navigation).await?;
129                    display_manager.fetch_all().await?;
130                    display_manager.render().await?;
131                }
132            }
133        }
134    }
135}