graphics/
manager.rs

1use alloc::{
2    string::{String, ToString},
3    vec,
4    vec::Vec,
5};
6use file_system::DirectCharacterDevice;
7use synchronization::blocking_mutex::raw::CriticalSectionRawMutex;
8use synchronization::mutex::{Mutex, MutexGuard};
9use synchronization::{once_lock::OnceLock, rwlock::RwLock};
10use task::block_on;
11
12use core::{future::Future, mem::forget};
13
14use core::time::Duration;
15
16use super::lvgl;
17
18use crate::Display;
19use crate::Input;
20use crate::InputKind;
21use crate::window::Window;
22use crate::{Color, theme};
23use crate::{Error, Result};
24
25static MANAGER_INSTANCE: OnceLock<Manager> = OnceLock::new();
26
27pub async fn initialize(
28    screen_device: &'static dyn DirectCharacterDevice,
29    input_device: &'static dyn DirectCharacterDevice,
30    input_device_type: InputKind,
31    buffer_size: usize,
32    double_buffered: bool,
33) -> &'static Manager {
34    let manager = Manager::new(
35        time::get_instance(),
36        screen_device,
37        input_device,
38        input_device_type,
39        buffer_size,
40        double_buffered,
41    )
42    .expect("Failed to create manager instance");
43
44    MANAGER_INSTANCE.get_or_init(|| manager)
45}
46
47pub fn get_instance() -> &'static Manager {
48    MANAGER_INSTANCE
49        .try_get()
50        .expect("Graphics manager not initialized")
51}
52
53pub fn try_get_instance() -> Option<&'static Manager> {
54    MANAGER_INSTANCE.try_get()
55}
56
57struct Inner {
58    _inputs: Vec<Input>,
59    displays: Vec<Display>,
60    window_parent: *mut lvgl::lv_obj_t,
61}
62
63pub struct Manager {
64    inner: RwLock<CriticalSectionRawMutex, Inner>,
65    global_lock: Mutex<CriticalSectionRawMutex, ()>,
66}
67
68impl Drop for Manager {
69    fn drop(&mut self) {
70        unsafe {
71            lvgl::lv_deinit();
72        }
73    }
74}
75
76extern "C" fn binding_tick_callback_function() -> u32 {
77    time::get_instance()
78        .get_current_time()
79        .unwrap_or_default()
80        .as_millis() as u32
81}
82
83unsafe impl Send for Manager {}
84
85unsafe impl Sync for Manager {}
86
87impl Manager {
88    fn new(
89        _: &time::Manager,
90        screen_device: &'static dyn DirectCharacterDevice,
91        input_device: &'static dyn DirectCharacterDevice,
92        input_device_type: InputKind,
93        buffer_size: usize,
94        double_buffered: bool,
95    ) -> Result<Self> {
96        unsafe {
97            lvgl::lv_init();
98
99            if !lvgl::lv_is_initialized() {
100                panic!("Failed to initialize lvgl");
101            }
102
103            lvgl::lv_tick_set_cb(Some(binding_tick_callback_function));
104        }
105
106        let (display, input) = Self::create_display(
107            screen_device,
108            buffer_size,
109            input_device,
110            input_device_type,
111            double_buffered,
112        )?;
113
114        let screen = display.get_object();
115
116        unsafe {
117            let group = lvgl::lv_group_create();
118            lvgl::lv_group_set_default(group);
119            theme::initialize(&display);
120        }
121
122        Ok(Self {
123            inner: RwLock::new(Inner {
124                _inputs: vec![input],
125                displays: vec![display],
126                window_parent: screen,
127            }),
128            global_lock: Mutex::new(()),
129        })
130    }
131
132    pub async fn r#loop<F>(&self, sleep: impl Fn(Duration) -> F + Send + 'static) -> Result<()>
133    where
134        F: Future<Output = ()> + Send + 'static,
135    {
136        let mut call_count: u8 = 0;
137
138        loop {
139            let time_until_next = unsafe {
140                let _lock = self.global_lock.lock().await;
141                let inner = self.inner.read().await;
142
143                let time_until_next = lvgl::lv_timer_handler();
144
145                if call_count >= 30 {
146                    for display in &inner.displays {
147                        display.check_for_resizing();
148                    }
149                    call_count = 0;
150                }
151                call_count = call_count.saturating_add(1);
152
153                time_until_next
154            };
155
156            sleep(Duration::from_millis(time_until_next as u64)).await;
157        }
158    }
159
160    pub async fn set_window_parent(&self, window_parent: *mut lvgl::lv_obj_t) -> Result<()> {
161        self.inner.write().await.window_parent = window_parent;
162
163        Ok(())
164    }
165
166    pub async fn create_window(&self) -> Result<Window> {
167        let parent_object = self.inner.write().await.window_parent;
168
169        let window = unsafe { Window::new(parent_object)? };
170
171        Ok(window)
172    }
173
174    pub async fn add_input_device(
175        &self,
176
177        input_device: &'static dyn DirectCharacterDevice,
178        input_type: InputKind,
179    ) -> Result<()> {
180        let input = Input::new(input_device, input_type)?;
181
182        self.inner.write().await._inputs.push(input);
183
184        Ok(())
185    }
186
187    fn create_display(
188        screen_device: &'static dyn DirectCharacterDevice,
189        buffer_size: usize,
190        input_device: &'static dyn DirectCharacterDevice,
191        input_device_type: InputKind,
192        double_buffered: bool,
193    ) -> Result<(Display, Input)> {
194        let display = Display::new(screen_device, buffer_size, double_buffered)?;
195
196        let input = Input::new(input_device, input_device_type)?;
197
198        Ok((display, input))
199    }
200
201    pub async fn get_window_count(&self) -> Result<usize> {
202        let window_parent = self.inner.read().await.window_parent;
203        unsafe { Ok(lvgl::lv_obj_get_child_count(window_parent) as usize) }
204    }
205
206    pub async fn get_window_icon(&self, index: usize) -> Result<(String, Color)> {
207        let window_parent = self.inner.read().await.window_parent;
208
209        let window = unsafe {
210            let child = lvgl::lv_obj_get_child(window_parent, index as i32);
211
212            Window::from_raw(child)
213        };
214
215        let icon = window.get_icon();
216
217        let icon = (icon.0.to_string(), icon.1);
218
219        forget(window);
220
221        Ok(icon)
222    }
223
224    pub async fn get_window_identifier(&self, index: usize) -> Result<usize> {
225        let window_parent = self.inner.read().await.window_parent;
226
227        let window = unsafe { lvgl::lv_obj_get_child(window_parent, index as i32) as usize };
228
229        Ok(window)
230    }
231
232    pub async fn maximize_window(&self, identifier: usize) -> Result<()> {
233        let window_count = self.get_window_count().await?;
234
235        let window_parent = self.inner.read().await.window_parent;
236
237        let found = (0..window_count).find(|index| unsafe {
238            let child = lvgl::lv_obj_get_child(window_parent, *index as i32);
239
240            child == identifier as *mut lvgl::lv_obj_t
241        });
242
243        if found.is_some() {
244            unsafe {
245                lvgl::lv_obj_move_foreground(identifier as *mut lvgl::lv_obj_t);
246            }
247
248            Ok(())
249        } else {
250            Err(Error::InvalidWindowIdentifier)
251        }
252    }
253
254    pub async fn lock_function<T>(&self, function: impl FnOnce() -> Result<T>) -> Result<T> {
255        let _lock = self.global_lock.lock().await;
256
257        function()
258    }
259
260    pub async fn lock(&self) -> MutexGuard<'_, CriticalSectionRawMutex, ()> {
261        self.global_lock.lock().await
262    }
263
264    pub fn synchronous_lock(&self) -> MutexGuard<'_, CriticalSectionRawMutex, ()> {
265        block_on(self.lock())
266    }
267
268    pub fn try_lock(&self) -> Option<MutexGuard<'_, CriticalSectionRawMutex, ()>> {
269        self.global_lock.try_lock().ok()
270    }
271
272    pub fn get_current_screen(&self) -> Result<*mut lvgl::lv_obj_t> {
273        Ok(unsafe { lvgl::lv_screen_active() })
274    }
275
276    pub async fn update_theme(
277        &self,
278        primary_color: Color,
279        secondary_color: Color,
280        is_dark: bool,
281    ) -> Result<()> {
282        let displays = &self.inner.read().await.displays;
283
284        for display in displays {
285            theme::update(display, primary_color, secondary_color, is_dark);
286        }
287
288        Ok(())
289    }
290}