1#![allow(dead_code)]
15#![deny(
17 clippy::unwrap_used,
18 clippy::expect_used,
19 clippy::panic,
20 clippy::indexing_slicing
21)]
22
23use alloc::collections::VecDeque;
24use alloc::string::String;
25use alloc::sync::Arc;
26use alloc::vec::Vec;
27use spin::Mutex;
28
29pub const EV_KEY: u64 = 1 << 56;
34pub const EV_MOUSE: u64 = 2 << 56;
35pub const EV_CLOSED: u64 = 3 << 56;
36
37const MAX_EVENTS: usize = 64;
38const MAX_DIM: u32 = 2048;
39
40pub struct UserWinShared {
42 pub width: u32,
43 pub height: u32,
44 pub pixels: Vec<u32>,
46 pub events: VecDeque<u64>,
48 pub open: bool,
50 pub fresh: bool,
52}
53
54struct Registry {
55 next_handle: usize,
56 wins: Vec<(usize, Arc<Mutex<UserWinShared>>)>,
57}
58
59static REGISTRY: Mutex<Registry> = Mutex::new(Registry {
60 next_handle: 1,
61 wins: Vec::new(),
62});
63
64fn lookup(handle: usize) -> Option<Arc<Mutex<UserWinShared>>> {
65 let reg = REGISTRY.lock();
66 reg.wins
67 .iter()
68 .find(|(h, _)| *h == handle)
69 .map(|(_, s)| s.clone())
70}
71
72pub struct UserApp {
74 shared: Arc<Mutex<UserWinShared>>,
75 title: String,
76}
77
78impl crate::kernel::window_mgr::App for UserApp {
79 fn name(&self) -> &str {
80 "user_app"
81 }
82
83 fn instance_info(&self) -> String {
84 self.title.clone()
85 }
86
87 fn draw(
88 &mut self,
89 screen: &crate::kernel::draw::Screen,
90 win_x: u32,
91 win_y: u32,
92 win_w: u32,
93 win_h: u32,
94 ) {
95 let sh = self.shared.lock();
96 let t = &crate::kernel::config::get_config().theme;
97 if !sh.open {
98 screen.boxfill(
100 win_x,
101 win_y,
102 win_x + win_w,
103 win_y + win_h,
104 crate::kernel::draw::Color(t.ui_overlay_bg),
105 );
106 screen.draw_string_vector(
107 win_x + 16,
108 win_y + 16,
109 "Process Terminated",
110 crate::kernel::draw::Color(t.error_fg),
111 16,
112 );
113 return;
114 }
115 screen.boxfill(
117 win_x,
118 win_y,
119 win_x + win_w,
120 win_y + win_h,
121 crate::kernel::draw::Color(t.terminal_bg),
122 );
123 let bw = sh.width.min(win_w);
125 let bh = sh.height.min(win_h);
126 for row in 0..bh {
127 for col in 0..bw {
128 let px = sh
129 .pixels
130 .get((row * sh.width + col) as usize)
131 .copied()
132 .unwrap_or(0xFF000000);
133 screen.draw_pixel(win_x + col, win_y + row, crate::kernel::draw::Color(px));
134 }
135 }
136 }
137
138 fn on_key(&mut self, keycode: u8, pressed: bool, ascii: Option<char>) {
139 let mut sh = self.shared.lock();
140 if sh.events.len() >= MAX_EVENTS {
141 sh.events.pop_front();
142 }
143 let mut ev = EV_KEY | (keycode as u64) | ((pressed as u64) << 8);
144 if let Some(c) = ascii {
145 ev |= ((c as u32 as u64) & 0xFFFF) << 16;
146 }
147 sh.events.push_back(ev);
148 }
149
150 fn on_mouse(
151 &mut self,
152 local_x: i32,
153 local_y: i32,
154 btn_left: bool,
155 _btn_right: bool,
156 wheel: i32,
157 ) {
158 let mut sh = self.shared.lock();
159 if sh.events.len() >= MAX_EVENTS {
160 sh.events.pop_front();
161 }
162 let ev = EV_MOUSE
163 | (local_x as i16 as u16 as u64)
164 | ((local_y as i16 as u16 as u64) << 16)
165 | ((btn_left as u64) << 32)
166 | (((wheel as i8 as u8) as u64) << 40);
167 sh.events.push_back(ev);
168 }
169
170 fn close_tab(&mut self, _idx: usize) -> bool {
171 let mut sh = self.shared.lock();
173 sh.open = false;
174 sh.events.push_back(EV_CLOSED);
175 true
176 }
177}
178
179impl Drop for UserApp {
180 fn drop(&mut self) {
183 let mut sh = self.shared.lock();
184 if sh.open {
185 sh.open = false;
186 sh.events.push_back(EV_CLOSED);
187 }
188 }
189}
190
191use crate::kernel::usercopy;
196
197pub fn sys_win_create(req_ptr: usize) -> usize {
203 let Some(&[title_ptr, title_len, w_raw, h_raw]) = usercopy::user_slice::<usize>(req_ptr, 4)
204 else {
205 return 0;
206 };
207 let (w, h) = (w_raw as u32, h_raw as u32);
208
209 if w == 0 || h == 0 || w > MAX_DIM || h > MAX_DIM {
210 return 0;
211 }
212
213 let title = match usercopy::user_str(title_ptr, title_len, 64) {
214 Some(s) => String::from(s),
215 None => return 0,
216 };
217
218 let need = (w as usize * h as usize * 4)
220 .saturating_mul(2)
221 .saturating_add(64 * 1024);
222 let (used, total) = crate::kernel::allocator::get_heap_stats();
223 if total.saturating_sub(used) < need {
224 return 0;
225 }
226
227 let shared = Arc::new(Mutex::new(UserWinShared {
228 width: w,
229 height: h,
230 pixels: alloc::vec![0xFF000000u32; (w * h) as usize],
231 events: VecDeque::new(),
232 open: true,
233 fresh: false,
234 }));
235
236 let handle = {
237 let mut reg = REGISTRY.lock();
238 let h_id = reg.next_handle;
239 reg.next_handle += 1;
240 reg.wins.push((h_id, shared.clone()));
241 h_id
242 };
243
244 let app = UserApp {
245 shared,
246 title: title.clone(),
247 };
248 let win = crate::kernel::window_mgr::Window::new(
250 160,
251 120,
252 w + 8,
253 h + 40,
254 &title,
255 alloc::boxed::Box::new(app),
256 );
257 crate::kernel::window_mgr::get_instance().add_window(win);
258
259 crate::info!(
260 "[user_win] EL0 window created: handle={} '{}' {}x{}",
261 handle,
262 title,
263 w,
264 h
265 );
266 handle
267}
268
269pub fn sys_win_blit(handle: usize, pix_ptr: usize) -> usize {
275 let Some(shared) = lookup(handle) else {
276 return usize::MAX;
277 };
278 let mut sh = shared.lock();
279 if !sh.open {
280 return usize::MAX;
281 }
282 let count = (sh.width * sh.height) as usize;
283 let Some(src) = usercopy::user_slice::<u32>(pix_ptr, count) else {
284 return usize::MAX;
285 };
286 sh.pixels.copy_from_slice(src);
287 sh.fresh = true;
288 drop(sh);
289 crate::kernel::window_mgr::get_instance().dirty = true;
291 0
292}
293
294pub fn sys_win_poll(handle: usize, out_ptr: usize) -> usize {
299 if !usercopy::user_ptr_ok::<u64>(out_ptr, 1) {
300 return usize::MAX;
301 }
302 let Some(shared) = lookup(handle) else {
303 return usize::MAX;
304 };
305 let mut sh = shared.lock();
306 if let Some(ev) = sh.events.pop_front() {
307 usercopy::write_user::<u64>(out_ptr, ev);
308 1
309 } else if !sh.open {
310 usercopy::write_user::<u64>(out_ptr, EV_CLOSED);
311 1
312 } else {
313 0
314 }
315}
316
317pub fn sys_win_close(handle: usize) -> usize {
319 let Some(shared) = lookup(handle) else {
320 return usize::MAX;
321 };
322 {
323 let mut sh = shared.lock();
324 sh.open = false;
325 }
326 {
327 let mut reg = REGISTRY.lock();
328 reg.wins.retain(|(h, _)| *h != handle);
329 }
330 crate::kernel::window_mgr::get_instance().dirty = true;
331 0
332}