1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/// Definitions of button structs
pub mod button;

/// Methods for interacting with the core
mod methods;
pub mod manager;

use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::{channel, Receiver};
use streamdeck::{Kind, StreamDeck};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::config::{Config, UniqueDeviceConfig};
use crate::core::button::Button;
use crate::thread::{DeviceThreadCommunication, DeviceThreadHandle, spawn_device_thread};
use crate::ImageCollection;
use crate::modules::events::SDGlobalEvent;
use crate::modules::ModuleManager;
use crate::socket::{send_event_to_socket, SocketManager};
use crate::thread::rendering::custom::RenderingManager;

pub use methods::CoreHandle;
pub use methods::check_feature_list_for_feature;
pub use methods::warn_for_feature;

/// Reference counted RwLock of a button, prevents data duplication and lets you edit buttons if they're in many stacks at once
pub type UniqueButton = Arc<RwLock<Button>>;

/// Map of UniqueButtons
pub type UniqueButtonMap = HashMap<u8, UniqueButton>;

/// Map of Buttons
pub type ButtonMap = HashMap<u8, Button>;

/// Hashmap of UniqueButtons
pub type ButtonPanel = Arc<RwLock<Panel<UniqueButtonMap>>>;

/// Hashmap of raw Buttons
pub type RawButtonPanel = Panel<ButtonMap>;

/// Panel definition
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
pub struct Panel<T> {
    /// Display name that will be shown in UI
    #[serde(default)]
    pub display_name: String,
    /// Data to keep with stack
    #[serde(default)]
    pub data: Value,
    /// Buttons of the panel
    #[serde(default)]
    pub buttons: T
}

impl Into<ButtonMap> for Panel<ButtonMap> {
    fn into(self) -> ButtonMap {
        self.buttons
    }
}

impl Into<ButtonMap> for &Panel<ButtonMap> {
    fn into(self) -> ButtonMap {
        self.buttons.clone()
    }
}

impl Into<UniqueButtonMap> for Panel<UniqueButtonMap> {
    fn into(self) -> UniqueButtonMap {
        self.buttons
    }
}

impl Into<UniqueButtonMap> for &Panel<UniqueButtonMap> {
    fn into(self) -> UniqueButtonMap {
        self.buttons.clone()
    }
}

/// Core struct that contains all relevant information about streamdeck and manages the streamdeck
#[allow(dead_code)]
pub struct SDCore {
    /// Serial number of the device
    pub serial_number: String,

    /// Module manager
    pub module_manager: Arc<ModuleManager>,

    /// Rendering manager
    pub render_manager: Arc<RenderingManager>,

    /// Socket manager
    pub socket_manager: Arc<SocketManager>,

    /// Config
    pub config: Arc<Config>,

    /// Device config associated with the device
    pub device_config: UniqueDeviceConfig,

    /// Current panel stack
    pub current_stack: Mutex<Vec<ButtonPanel>>,

    /// Image size supported by streamdeck
    pub image_size: (usize, usize),

    /// Image collection to use for thread
    pub image_collection: ImageCollection,

    /// Kind of streamdeck device
    pub kind: Kind,

    /// Key count of the streamdeck device
    pub key_count: u8,

    /// Pool rate of how often should the core read events from the device
    pub frame_rate: u32,

    /// Decides if core is dead
    pub should_close: RwLock<bool>,

    handles: Mutex<Option<ThreadHandles>>
}

impl SDCore {
    /// Creates an instance of core that is already dead
    pub 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> {
        let serial_number = device_config.read().unwrap().serial.to_string();
        Arc::new(SDCore {
            serial_number,
            module_manager,
            render_manager,
            socket_manager,
            config,
            device_config,
            current_stack: Mutex::new(vec![]),
            handles: Mutex::new(None),
            image_size: (0, 0),
            image_collection,
            kind: Kind::Original,
            key_count: 0,
            frame_rate: 0,
            should_close: RwLock::new(true)
        })
    }

    /// Creates an instance of the core over existing streamdeck connection
    pub 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>, KeyHandler) {
        let (key_tx, key_rx) = channel();

        let serial_number = connection.serial().unwrap_or_else(|_| device_config.read().unwrap().serial.to_string());

        send_event_to_socket(&socket_manager, SDGlobalEvent::DeviceConnected {
            serial_number: serial_number.clone()
        });

        let core = Arc::new(SDCore {
            serial_number,
            module_manager,
            render_manager,
            socket_manager,
            config,
            device_config,
            current_stack: Mutex::new(vec![]),
            handles: Mutex::new(None),
            image_size: connection.image_size(),
            image_collection,
            kind: connection.kind(),
            key_count: connection.kind().keys(),
            frame_rate,
            should_close: RwLock::new(false)
        });

        let renderer = spawn_device_thread(core.clone(), connection, key_tx);

        if let Ok(mut handles) = core.handles.lock() {
            *handles = Some(
                ThreadHandles {
                    renderer
                }
            )
        }

        (core.clone(), KeyHandler {
            core: CoreHandle::wrap(core.clone()),
            receiver: key_rx
        })
    }

    /// Tells device thread to refresh screen
    pub fn mark_for_redraw(&self) {
        let handles = self.handles.lock().unwrap();

        handles.as_ref().unwrap().renderer.send(vec![DeviceThreadCommunication::RefreshScreen]);
    }

    /// Sends commands to streamdeck thread
    pub fn send_commands(&self, commands: Vec<DeviceThreadCommunication>) {
        let handles = self.handles.lock().unwrap();

        handles.as_ref().unwrap().renderer.send(commands);
    }

    /// Gets serial number of the core
    pub fn serial_number(&self) -> String {
        self.device_config.read().unwrap().serial.to_string()
    }

    /// Checks if core is supposed to be closed
    pub fn is_closed(&self) -> bool {
        *self.should_close.read().unwrap()
    }

    /// Kills the core and all the related threads
    pub fn close(&self) {
        send_event_to_socket(&self.socket_manager, SDGlobalEvent::DeviceDisconnected {
            serial_number: self.serial_number.to_string()
        });

        let mut lock = self.should_close.write().unwrap();
        *lock = true;
    }
}

struct ThreadHandles {
    pub renderer: DeviceThreadHandle
}

/// Routine that acts as a middleman between streamdeck thread and the core, was made as a way to get around Sync restriction
pub struct KeyHandler {
    core: CoreHandle,
    receiver: Receiver<(u8, bool)>
}

impl KeyHandler {
    /// Runs the key handling loop in current thread
    pub fn run_loop(&self) {
        loop {
            if self.core.core().is_closed() {
                break
            }

            if let Ok((key, state)) = self.receiver.recv() {
                if state {
                    self.core.button_down(key);
                } else {
                    self.core.button_up(key);
                }
            } else {
                break;
            }
        }
    }
}