Skip to main content

graphics/
window.rs

1use super::lvgl;
2use crate::{Color, Error, EventKind, Result, event::Event};
3use alloc::boxed::Box;
4use alloc::collections::VecDeque;
5use core::{mem::forget, str};
6
7struct UserData {
8    pub queue: VecDeque<Event>,
9    pub icon_text: [u8; 2],
10    pub icon_color: Color,
11}
12
13pub struct Window {
14    window: *mut lvgl::lv_obj_t,
15}
16
17impl Drop for Window {
18    fn drop(&mut self) {
19        if !self.is_valid() {
20            return;
21        }
22
23        unsafe {
24            let user_data = lvgl::lv_obj_get_user_data(self.window) as *mut UserData;
25
26            let _user_data = Box::from_raw(user_data);
27
28            lvgl::lv_obj_delete(self.window);
29        }
30    }
31}
32
33unsafe extern "C" fn event_callback(event: *mut lvgl::lv_event_t) {
34    unsafe {
35        let code = lvgl::lv_event_get_code(event);
36
37        let queue = lvgl::lv_event_get_user_data(event) as *mut VecDeque<Event>;
38
39        let target = lvgl::lv_event_get_target(event) as *mut lvgl::lv_obj_t;
40
41        match code {
42            lvgl::lv_event_code_t_LV_EVENT_CHILD_CREATED => {
43                lvgl::lv_obj_add_flag(target, lvgl::lv_obj_flag_t_LV_OBJ_FLAG_EVENT_BUBBLE);
44
45                (*queue).push_back(Event::new(EventKind::ChildCreated, target, None));
46            }
47            lvgl::lv_event_code_t_LV_EVENT_DRAW_MAIN
48            | lvgl::lv_event_code_t_LV_EVENT_DRAW_MAIN_BEGIN
49            | lvgl::lv_event_code_t_LV_EVENT_DRAW_MAIN_END
50            | lvgl::lv_event_code_t_LV_EVENT_DRAW_POST
51            | lvgl::lv_event_code_t_LV_EVENT_DRAW_POST_BEGIN
52            | lvgl::lv_event_code_t_LV_EVENT_DRAW_POST_END
53            | lvgl::lv_event_code_t_LV_EVENT_GET_SELF_SIZE
54            | lvgl::lv_event_code_t_LV_EVENT_COVER_CHECK => {
55                // Ignore draw events
56            }
57            lvgl::lv_event_code_t_LV_EVENT_KEY => {
58                let key = lvgl::lv_indev_get_key(lvgl::lv_indev_active());
59
60                (*queue).push_back(Event::new(EventKind::Key, target, Some(key.into())));
61            }
62            _ => {
63                (*queue).push_back(Event::new(EventKind::from_lvgl_code(code), target, None));
64            }
65        }
66    }
67}
68
69impl Window {
70    fn is_valid(&self) -> bool {
71        unsafe { lvgl::lv_obj_is_valid(self.window) }
72    }
73
74    /// Create a new window.
75    ///
76    /// # Arguments
77    ///
78    /// * `Parent_object` - The parent object of the window.
79    ///
80    /// # Returns
81    ///
82    /// * `Result<Self>` - The result of the operation.
83    ///
84    /// # Safety
85    ///
86    /// This function is unsafe because it may dereference raw pointers (e.g. `Parent_object`).
87    ///
88    pub unsafe fn new(parent_object: *mut lvgl::lv_obj_t) -> Result<Self> {
89        let window = unsafe { lvgl::lv_obj_create(parent_object) };
90
91        if window.is_null() {
92            return Err(Error::FailedToCreateObject);
93        }
94
95        let user_data = UserData {
96            queue: VecDeque::with_capacity(10),
97            icon_text: [b'I', b'c'],
98            icon_color: Color::BLACK,
99        };
100
101        let mut user_data = Box::new(user_data);
102
103        unsafe {
104            // Set the event callback for the window.
105            lvgl::lv_obj_add_event_cb(
106                window,
107                Some(event_callback),
108                lvgl::lv_event_code_t_LV_EVENT_ALL,
109                &mut user_data.queue as *mut _ as *mut core::ffi::c_void,
110            );
111            lvgl::lv_obj_add_flag(window, lvgl::lv_obj_flag_t_LV_OBJ_FLAG_EVENT_BUBBLE);
112            lvgl::lv_obj_set_user_data(window, Box::into_raw(user_data) as *mut core::ffi::c_void);
113            // Set the size of the window to 100% of the parent object.
114            lvgl::lv_obj_set_size(window, lvgl::lv_pct(100), lvgl::lv_pct(100));
115            lvgl::lv_obj_set_style_border_width(window, 0, lvgl::LV_STATE_DEFAULT);
116            lvgl::lv_obj_set_style_radius(window, 0, lvgl::LV_STATE_DEFAULT);
117        }
118
119        Ok(Self { window })
120    }
121
122    pub fn get_identifier(&self) -> usize {
123        self.window as usize
124    }
125
126    pub fn peek_event(&self) -> Option<Event> {
127        if !self.is_valid() {
128            return None;
129        }
130
131        let user_data = unsafe { lvgl::lv_obj_get_user_data(self.window) as *mut UserData };
132
133        let user_data = unsafe { Box::from_raw(user_data) };
134
135        let event = user_data.queue.front().cloned();
136
137        forget(user_data);
138
139        event
140    }
141
142    pub fn pop_event(&mut self) -> Option<Event> {
143        if !self.is_valid() {
144            return None;
145        }
146
147        let user_data = unsafe { lvgl::lv_obj_get_user_data(self.window) as *mut UserData };
148
149        let mut user_data = unsafe { Box::from_raw(user_data) };
150
151        let event = user_data.queue.pop_front();
152
153        forget(user_data);
154
155        event
156    }
157
158    pub fn get_object(&self) -> *mut lvgl::lv_obj_t {
159        self.window
160    }
161
162    pub fn get_icon(&self) -> (&str, Color) {
163        let user_data = unsafe {
164            let user_data = lvgl::lv_obj_get_user_data(self.window) as *mut UserData;
165
166            &*user_data
167        };
168
169        unsafe {
170            (
171                str::from_utf8_unchecked(&user_data.icon_text),
172                user_data.icon_color,
173            )
174        }
175    }
176
177    pub fn set_icon(&mut self, icon_string: &str, icon_color: Color) {
178        let user_data = unsafe { lvgl::lv_obj_get_user_data(self.window) as *mut UserData };
179
180        let user_data = unsafe { &mut *user_data };
181
182        let mut iterator = icon_string.chars();
183
184        if let Some(character) = iterator.next() {
185            user_data.icon_text[0] = character as u8;
186        }
187
188        if let Some(character) = iterator.next() {
189            user_data.icon_text[1] = character as u8;
190        }
191
192        user_data.icon_color = icon_color;
193    }
194
195    /// Convert a raw pointer to a window object.
196    ///
197    /// # Returns
198    ///
199    /// * `Window` - The raw pointer to the window.
200    ///
201    /// # Safety
202    ///
203    /// This function is unsafe because it may dereference raw pointers (e.g. `Window`).
204    ///
205    pub unsafe fn from_raw(window: *mut lvgl::lv_obj_t) -> Self {
206        Self { window }
207    }
208
209    pub fn into_raw(self) -> *mut lvgl::lv_obj_t {
210        let window = self.window;
211
212        forget(self);
213
214        window
215    }
216}