Skip to main content

graphics/
window.rs

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        // Appelle explicitement Drop sur l'ensemble des champs Rust de Window
90        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; // Not our event, ignore it
103        }
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                // Ignore draw events
130            }
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    /// Create a new window.
143    ///
144    /// # Arguments
145    ///
146    /// * `Parent_object` - The parent object of the window.
147    ///
148    /// # Returns
149    ///
150    /// * `Result<Self>` - The result of the operation.
151    ///
152    /// # Safety
153    ///
154    /// This function is unsafe because it may dereference raw pointers (e.g. `Parent_object`).
155    ///
156    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    /// Convert a raw pointer to a window object.
220    ///
221    /// # Returns
222    ///
223    /// * `Window` - The raw pointer to the window.
224    ///
225    /// # Safety
226    ///
227    /// This function is unsafe because it may dereference raw pointers (e.g. `Window`).
228    ///
229    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
293/// A wrapper around Window that automatically deletes the window when dropped.
294pub 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}