Skip to main content

graphics/
window.rs

1use super::lvgl;
2use crate::{Color, Error, EventKind, Key, Result, event::Event};
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    /// Forget queued targets before their LVGL storage can be reused. This only
180    /// compares addresses; it never dereferences the potentially deleted target.
181    pub fn discard_events_for_target(&mut self, target: *mut lvgl::lv_obj_t) {
182        self.event_queue.retain(|event| event.target != target);
183    }
184
185    /// Preserve event codes while preventing queued addresses from acquiring a
186    /// newly registered capability when native object storage is recycled.
187    /// Only compares addresses; it does not dereference event targets.
188    pub fn clear_event_targets_for(&mut self, target: *mut lvgl::lv_obj_t) {
189        for event in &mut self.event_queue {
190            if event.target == target {
191                event.target = ptr::null_mut();
192            }
193        }
194    }
195
196    pub fn as_object(&self) -> &lvgl::lv_obj_t {
197        &self.object
198    }
199
200    pub fn as_object_mutable(&mut self) -> &mut lvgl::lv_obj_t {
201        &mut self.object
202    }
203
204    pub fn get_icon(&self) -> (&str, Color) {
205        let icon_string = str::from_utf8(&self.icon_text)
206            .unwrap_or("??")
207            .trim_matches(char::from(0));
208
209        (icon_string, self.icon_color)
210    }
211
212    pub fn set_icon(&mut self, icon_string: &str, icon_color: Color) {
213        let mut iterator = icon_string.chars();
214
215        if let Some(character) = iterator.next() {
216            self.icon_text[0] = character as u8;
217        }
218
219        if let Some(character) = iterator.next() {
220            self.icon_text[1] = character as u8;
221        }
222
223        self.icon_color = icon_color;
224    }
225
226    pub fn wake_up(&mut self) {
227        self.waker.wake();
228    }
229
230    pub fn register_waker(&mut self, waker: &core::task::Waker) {
231        self.waker.register(waker);
232    }
233
234    /// Convert a raw pointer to a window object.
235    ///
236    /// # Returns
237    ///
238    /// * `Window` - The raw pointer to the window.
239    ///
240    /// # Safety
241    ///
242    /// `window` must be null or point to a live LVGL object.
243    ///
244    pub unsafe fn from_raw(window: *mut lvgl::lv_obj_t) -> Option<NonNull<Self>> {
245        if window.is_null() {
246            return None;
247        }
248
249        let class = unsafe { lvgl::lv_obj_get_class(window) };
250
251        if class != get_window_class() {
252            return None;
253        }
254
255        NonNull::new(window as *mut Self)
256    }
257
258    pub fn delete(&mut self) {
259        unsafe {
260            lvgl::lv_obj_delete(&mut self.object);
261        }
262    }
263}
264
265impl AsRef<lvgl::lv_obj_t> for Window {
266    #[inline]
267    fn as_ref(&self) -> &lvgl::lv_obj_t {
268        &self.object
269    }
270}
271
272impl AsMut<lvgl::lv_obj_t> for Window {
273    #[inline]
274    fn as_mut(&mut self) -> &mut lvgl::lv_obj_t {
275        &mut self.object
276    }
277}
278
279impl Deref for Window {
280    type Target = lvgl::lv_obj_t;
281
282    #[inline]
283    fn deref(&self) -> &Self::Target {
284        &self.object
285    }
286}
287
288impl DerefMut for Window {
289    #[inline]
290    fn deref_mut(&mut self) -> &mut Self::Target {
291        &mut self.object
292    }
293}
294
295/// A wrapper around Window that automatically deletes the window when dropped.
296pub struct OwnedWindow(NonNull<Window>);
297
298impl OwnedWindow {
299    pub fn new(window: NonNull<Window>) -> Self {
300        Self(window)
301    }
302}
303
304impl From<OwnedWindow> for NonNull<Window> {
305    fn from(val: OwnedWindow) -> Self {
306        let this = ManuallyDrop::new(val);
307        this.0
308    }
309}
310
311impl Drop for OwnedWindow {
312    fn drop(&mut self) {
313        crate::ffi_manager()
314            .lock_function_sync(|| {
315                unsafe {
316                    self.0.as_mut().delete();
317                }
318                Ok(())
319            })
320            .expect("Failed to delete window");
321    }
322}
323
324impl Deref for OwnedWindow {
325    type Target = Window;
326
327    #[inline]
328    fn deref(&self) -> &Self::Target {
329        unsafe { self.0.as_ref() }
330    }
331}
332
333impl DerefMut for OwnedWindow {
334    #[inline]
335    fn deref_mut(&mut self) -> &mut Self::Target {
336        unsafe { self.0.as_mut() }
337    }
338}