1use super::lvgl;
2use crate::{Color, Error, EventKind, Key, Result, event::Event, synchronous_lock};
3use alloc::collections::VecDeque;
4use core::{
5 future::poll_fn,
6 mem::ManuallyDrop,
7 ops::{Deref, DerefMut},
8 ptr::{self, NonNull},
9 str,
10 task::Poll,
11};
12use synchronization::{once_lock::OnceLock, waitqueue::AtomicWaker};
13
14const WINDOW_QUEUE_DEFAULT_CAPACITY: usize = 10;
15
16#[repr(transparent)]
17pub struct ClassWrapper(lvgl::lv_obj_class_t);
18
19unsafe impl Send for ClassWrapper {}
20unsafe impl Sync for ClassWrapper {}
21
22static WINDOW_CLASS: OnceLock<ClassWrapper> = OnceLock::new();
23
24pub fn get_window_class() -> &'static lvgl::lv_obj_class_t {
25 let class_ref = WINDOW_CLASS.get_or_init(|| {
26 let mut cls = lvgl::lv_obj_class_t {
27 base_class: unsafe { &lvgl::lv_obj_class },
28 constructor_cb: Some(window_constructor),
29 destructor_cb: Some(window_destructor),
30 event_cb: Some(window_event_callback),
31 width_def: unsafe { lvgl::lv_pct(100) },
32 height_def: unsafe { lvgl::lv_pct(100) },
33 name: c"window".as_ptr(),
34 ..Default::default()
35 };
36
37 cls.set_instance_size(size_of::<Window>() as _);
38 cls.set_group_def(lvgl::lv_obj_class_group_def_t_LV_OBJ_CLASS_GROUP_DEF_INHERIT as _);
39 cls.set_theme_inheritable(
40 lvgl::lv_obj_class_theme_inheritable_t_LV_OBJ_CLASS_THEME_INHERITABLE_TRUE as _,
41 );
42 cls.set_editable(lvgl::lv_obj_class_editable_t_LV_OBJ_CLASS_EDITABLE_INHERIT as _);
43
44 ClassWrapper(cls)
45 });
46
47 &class_ref.0
48}
49
50#[repr(C)]
51pub struct Window {
52 object: lvgl::lv_obj_t,
53 event_queue: VecDeque<Event>,
54 icon_text: [u8; 2],
55 icon_color: Color,
56 waker_registration: AtomicWaker,
57}
58
59unsafe extern "C" fn window_constructor(
60 _class_p: *const lvgl::lv_obj_class_t,
61 object: *mut lvgl::lv_obj_t,
62) {
63 unsafe {
64 let window = object as *mut Window;
65
66 ptr::write(
67 &mut (*window).event_queue,
68 VecDeque::with_capacity(WINDOW_QUEUE_DEFAULT_CAPACITY),
69 );
70 ptr::write(&mut (*window).icon_text, *b"Wi");
71 ptr::write(&mut (*window).icon_color, Color::BLACK);
72 ptr::write(&mut (*window).waker_registration, AtomicWaker::new());
73
74 lvgl::lv_obj_add_flag(
75 &mut (*window).object,
76 lvgl::lv_obj_flag_t_LV_OBJ_FLAG_EVENT_BUBBLE,
77 );
78 lvgl::lv_obj_set_style_border_width(&mut (*window).object, 0, lvgl::LV_STATE_DEFAULT);
79 lvgl::lv_obj_set_style_radius(&mut (*window).object, 0, lvgl::LV_STATE_DEFAULT);
80 }
81}
82
83unsafe extern "C" fn window_destructor(
84 _class_p: *const lvgl::lv_obj_class_t,
85 obj: *mut lvgl::lv_obj_t,
86) {
87 unsafe {
88 let window = obj as *mut Window;
89 core::ptr::drop_in_place(window);
91 }
92}
93
94unsafe extern "C" fn window_event_callback(
95 _class_p: *const lvgl::lv_obj_class_t,
96 event: *mut lvgl::lv_event_t,
97) {
98 unsafe {
99 let res = lvgl::lv_obj_event_base(get_window_class(), event);
100
101 if res != lvgl::lv_result_t_LV_RESULT_OK {
102 return; }
104
105 let code = lvgl::lv_event_get_code(event);
106
107 let window = lvgl::lv_event_get_current_target(event) as *mut Window;
108 let target = lvgl::lv_event_get_target(event) as *mut lvgl::lv_obj_t;
109
110 let (code, key) = match code {
111 lvgl::lv_event_code_t_LV_EVENT_CHILD_CREATED => {
112 lvgl::lv_obj_add_flag(target, lvgl::lv_obj_flag_t_LV_OBJ_FLAG_EVENT_BUBBLE);
113 (code, None)
114 }
115 lvgl::lv_event_code_t_LV_EVENT_KEY => {
116 let key = lvgl::lv_indev_get_key(lvgl::lv_indev_active());
117 let key = Key::from(key);
118 (code, Some(key))
119 }
120 lvgl::lv_event_code_t_LV_EVENT_DRAW_MAIN
121 | lvgl::lv_event_code_t_LV_EVENT_DRAW_MAIN_BEGIN
122 | lvgl::lv_event_code_t_LV_EVENT_DRAW_MAIN_END
123 | lvgl::lv_event_code_t_LV_EVENT_DRAW_POST
124 | lvgl::lv_event_code_t_LV_EVENT_DRAW_POST_BEGIN
125 | lvgl::lv_event_code_t_LV_EVENT_GET_SELF_SIZE
126 | lvgl::lv_event_code_t_LV_EVENT_COVER_CHECK
127 | lvgl::lv_event_code_t_LV_EVENT_LAYOUT_CHANGED => {
128 return;
129 }
131 _ => (code, None),
132 };
133
134 (*window)
135 .event_queue
136 .push_back(Event::new(EventKind::from_lvgl_code(code), target, key));
137 (*window).waker_registration.wake();
138 }
139}
140
141impl Window {
142 pub unsafe fn new(parent: *mut lvgl::lv_obj_t) -> Result<NonNull<Self>> {
157 let obj = unsafe { lvgl::lv_obj_class_create_obj(get_window_class(), parent) };
158
159 unsafe {
160 lvgl::lv_obj_class_init_obj(obj);
161 }
162
163 match NonNull::new(obj as *mut Self) {
164 Some(window) => Ok(window),
165 None => Err(Error::FailedToCreateObject),
166 }
167 }
168
169 pub fn get_identifier(&self) -> usize {
170 self as *const Self as usize
171 }
172
173 pub fn peek_event(&self) -> Option<Event> {
174 self.event_queue.front().cloned()
175 }
176
177 pub fn pop_event(&mut self) -> Option<Event> {
178 self.event_queue.pop_front()
179 }
180
181 pub fn as_object(&self) -> &lvgl::lv_obj_t {
182 &self.object
183 }
184
185 pub fn as_object_mutable(&mut self) -> &mut lvgl::lv_obj_t {
186 &mut self.object
187 }
188
189 pub fn get_icon(&self) -> (&str, Color) {
190 let icon_string = str::from_utf8(&self.icon_text)
191 .unwrap_or("??")
192 .trim_matches(char::from(0));
193
194 (icon_string, self.icon_color)
195 }
196
197 pub fn set_icon(&mut self, icon_string: &str, icon_color: Color) {
198 let mut iterator = icon_string.chars();
199
200 if let Some(character) = iterator.next() {
201 self.icon_text[0] = character as u8;
202 }
203
204 if let Some(character) = iterator.next() {
205 self.icon_text[1] = character as u8;
206 }
207
208 self.icon_color = icon_color;
209 }
210
211 pub fn wake_up(&mut self) {
212 self.waker_registration.wake();
213 }
214
215 pub fn register_waker(&mut self, waker: &core::task::Waker) {
216 self.waker_registration.register(waker);
217 }
218
219 pub unsafe fn from_raw(window: *mut lvgl::lv_obj_t) -> Option<NonNull<Self>> {
230 if !unsafe { lvgl::lv_obj_is_valid(window) } {
231 return None;
232 }
233
234 let class = unsafe { lvgl::lv_obj_get_class(window) };
235
236 if class != get_window_class() {
237 return None;
238 }
239
240 NonNull::new(window as *mut Self)
241 }
242
243 pub fn yield_now(&mut self) -> impl core::future::Future<Output = ()> + '_ {
244 let mut yielded = false;
245 poll_fn(move |cx| {
246 if yielded {
247 Poll::Ready(())
248 } else {
249 self.register_waker(cx.waker());
250 yielded = true;
251 Poll::Pending
252 }
253 })
254 }
255
256 pub fn delete(&mut self) {
257 unsafe {
258 lvgl::lv_obj_delete(&mut self.object);
259 }
260 }
261}
262
263impl AsRef<lvgl::lv_obj_t> for Window {
264 #[inline]
265 fn as_ref(&self) -> &lvgl::lv_obj_t {
266 &self.object
267 }
268}
269
270impl AsMut<lvgl::lv_obj_t> for Window {
271 #[inline]
272 fn as_mut(&mut self) -> &mut lvgl::lv_obj_t {
273 &mut self.object
274 }
275}
276
277impl Deref for Window {
278 type Target = lvgl::lv_obj_t;
279
280 #[inline]
281 fn deref(&self) -> &Self::Target {
282 &self.object
283 }
284}
285
286impl DerefMut for Window {
287 #[inline]
288 fn deref_mut(&mut self) -> &mut Self::Target {
289 &mut self.object
290 }
291}
292
293pub struct OwnedWindow(NonNull<Window>);
295
296impl OwnedWindow {
297 pub fn new(window: NonNull<Window>) -> Self {
298 Self(window)
299 }
300}
301
302impl From<NonNull<Window>> for OwnedWindow {
303 fn from(window: NonNull<Window>) -> Self {
304 Self::new(window)
305 }
306}
307
308impl From<OwnedWindow> for NonNull<Window> {
309 fn from(val: OwnedWindow) -> Self {
310 let this = ManuallyDrop::new(val);
311 this.0
312 }
313}
314
315impl Drop for OwnedWindow {
316 fn drop(&mut self) {
317 synchronous_lock!({
318 unsafe {
319 self.0.as_mut().delete();
320 }
321 });
322 }
323}
324
325impl Deref for OwnedWindow {
326 type Target = Window;
327
328 #[inline]
329 fn deref(&self) -> &Self::Target {
330 unsafe { self.0.as_ref() }
331 }
332}
333
334impl DerefMut for OwnedWindow {
335 #[inline]
336 fn deref_mut(&mut self) -> &mut Self::Target {
337 unsafe { self.0.as_mut() }
338 }
339}