Skip to main content

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;
13
14use core::time::Duration;
15
16use super::lvgl;
17
18use crate::Input;
19use crate::InputKind;
20use crate::window::Window;
21use crate::{Color, theme};
22use crate::{Display, OwnedWindow};
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<OwnedWindow> {
167        let parent_object = self.inner.write().await.window_parent;
168
169        let window = unsafe { Window::new(parent_object)? };
170
171        Ok(OwnedWindow::new(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).ok_or(Error::InvalidWindowIdentifier)?
213        };
214
215        let icon = unsafe { window.as_ref().get_icon() };
216
217        let icon = (icon.0.to_string(), icon.1);
218
219        Ok(icon)
220    }
221
222    pub async fn get_window_identifier(&self, index: usize) -> Result<usize> {
223        let window_parent = self.inner.read().await.window_parent;
224
225        let window = unsafe { lvgl::lv_obj_get_child(window_parent, index as i32) as usize };
226
227        Ok(window)
228    }
229
230    pub async fn maximize_window(&self, identifier: usize) -> Result<()> {
231        let window_count = self.get_window_count().await?;
232
233        let window_parent = self.inner.read().await.window_parent;
234
235        let found = (0..window_count).find(|index| unsafe {
236            let child = lvgl::lv_obj_get_child(window_parent, *index as i32);
237
238            child == identifier as *mut lvgl::lv_obj_t
239        });
240
241        if found.is_some() {
242            unsafe {
243                lvgl::lv_obj_move_foreground(identifier as *mut lvgl::lv_obj_t);
244            }
245
246            Ok(())
247        } else {
248            Err(Error::InvalidWindowIdentifier)
249        }
250    }
251
252    pub async fn send_window_close_request(&self, identifier: usize) -> Result<()> {
253        let window_count = self.get_window_count().await?;
254
255        let window_parent = self.inner.read().await.window_parent;
256
257        let found = (0..window_count).any(|index| unsafe {
258            let child = lvgl::lv_obj_get_child(window_parent, index as i32);
259
260            child == identifier as *mut lvgl::lv_obj_t
261        });
262
263        if !found {
264            return Err(Error::InvalidWindowIdentifier);
265        }
266
267        unsafe {
268            lvgl::lv_obj_send_event(
269                identifier as *mut lvgl::lv_obj_t,
270                crate::EventKind::CloseRequested.into_lvgl_code(),
271                core::ptr::null_mut(),
272            );
273        }
274
275        Ok(())
276    }
277
278    pub async fn lock_function<T>(&self, function: impl FnOnce() -> Result<T>) -> Result<T> {
279        let _lock = self.global_lock.lock().await;
280
281        function()
282    }
283
284    pub async fn lock(&self) -> MutexGuard<'_, CriticalSectionRawMutex, ()> {
285        self.global_lock.lock().await
286    }
287
288    pub fn synchronous_lock(&self) -> MutexGuard<'_, CriticalSectionRawMutex, ()> {
289        block_on(self.lock())
290    }
291
292    pub fn try_lock(&self) -> Option<MutexGuard<'_, CriticalSectionRawMutex, ()>> {
293        self.global_lock.try_lock().ok()
294    }
295
296    pub fn get_current_screen(&self) -> Result<*mut lvgl::lv_obj_t> {
297        Ok(unsafe { lvgl::lv_screen_active() })
298    }
299
300    pub async fn update_theme(
301        &self,
302        primary_color: Color,
303        secondary_color: Color,
304        is_dark: bool,
305    ) -> Result<()> {
306        let displays = &self.inner.read().await.displays;
307
308        for display in displays {
309            theme::update(display, primary_color, secondary_color, is_dark);
310        }
311
312        Ok(())
313    }
314}