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