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
29pub fn set_ffi_manager(manager: &'static Manager) {
31 FFI_GRAPHICS_MANAGER.store(manager as *const _ as *mut _, Ordering::Release);
32}
33
34pub 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}