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