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}