1use crate::{
25 hardware::video::{
26 graphics_adapter::{
27 get_adapter_for_display, total_display_count, Damage, DisplayScreen, GraphicsAdapter,
28 HeapScreen, SimpleDisplayAdapter,
29 },
30 Framebuffer,
31 },
32 syscall::error::SyscallError,
33 vfs::scheme::{
34 finalize_pseudo_stat, DirEntry, FileFlags, FileStat, OpenFlags, OpenResult, Scheme,
35 DEV_CHAR_FS, DT_CHR, DT_REG,
36 },
37};
38use alloc::{collections::BTreeMap, format, string::String, sync::Arc, vec, vec::Vec};
39use core::sync::atomic::{AtomicU64, Ordering};
40use spin::RwLock;
41
42const MAX_HANDLES: usize = 64;
43
44#[derive(Clone, Debug)]
45enum Handle {
46 Root,
47 Info,
48 Screen(u32, Arc<RwLock<HeapScreen>>),
50 Damage,
51 Clear,
52}
53
54pub struct DisplayScheme {
55 handles: RwLock<BTreeMap<u64, Handle>>,
56 next: AtomicU64,
57 screens: RwLock<BTreeMap<u32, Arc<RwLock<HeapScreen>>>>,
59}
60
61impl DisplayScheme {
62 fn new() -> Self {
63 Self {
64 handles: RwLock::new(BTreeMap::new()),
65 next: AtomicU64::new(1),
66 screens: RwLock::new(BTreeMap::new()),
67 }
68 }
69
70 fn alloc_id(&self) -> u64 {
71 self.next.fetch_add(1, Ordering::Relaxed)
72 }
73
74 fn get_or_create_screen(&self, display_id: u32) -> Arc<RwLock<HeapScreen>> {
76 let screens = self.screens.read();
77 if let Some(screen) = screens.get(&display_id) {
78 return screen.clone();
79 }
80 drop(screens);
81
82 let (w, h) = get_adapter_for_display(display_id as usize)
84 .map(|(a, local_id)| a.display_size(local_id))
85 .unwrap_or((800, 600));
86
87 let bpp = Framebuffer::info()
88 .map(|i| i.format.bits_per_pixel)
89 .unwrap_or(32);
90
91 let screen = Arc::new(RwLock::new(HeapScreen::new(w, h, bpp)));
92 self.screens.write().insert(display_id, screen.clone());
93 screen
94 }
95
96 fn parse_display_path(path: &str) -> Option<u32> {
98 let parts: Vec<&str> = path.split('.').collect();
99 if parts.len() != 2 {
100 return None;
101 }
102 let display_id: u32 = parts[0].parse().ok()?;
103 let _screen_id: u32 = parts[1].parse().ok()?;
104 Some(display_id)
106 }
107}
108
109impl Scheme for DisplayScheme {
110 fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
111 let path = path.trim_start_matches('/');
112 let id = self.alloc_id();
113
114 if self.handles.read().len() >= MAX_HANDLES {
115 return Err(SyscallError::OutOfMemory);
116 }
117
118 let handle = if path.is_empty() {
119 Handle::Root
120 } else if path == "info" {
121 Handle::Info
122 } else if path == "damage" {
123 Handle::Damage
124 } else if path == "clear" {
125 Handle::Clear
126 } else if let Some(display_id) = Self::parse_display_path(path) {
127 let screen = self.get_or_create_screen(display_id);
128 Handle::Screen(display_id, screen)
129 } else {
130 return Err(SyscallError::NotFound);
131 };
132
133 self.handles.write().insert(id, handle.clone());
134
135 let flags = match &handle {
136 Handle::Root => FileFlags::DIRECTORY,
137 Handle::Info => FileFlags::DEVICE,
138 Handle::Screen(..) => FileFlags::DEVICE | FileFlags::CHUNK_WRITE,
139 Handle::Damage | Handle::Clear => FileFlags::DEVICE | FileFlags::CHUNK_WRITE,
140 };
141
142 Ok(OpenResult {
143 file_id: id,
144 size: None,
145 flags,
146 })
147 }
148
149 fn read(&self, fid: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
150 let h = self.handles.read();
151 let handle = h.get(&fid).ok_or(SyscallError::BadHandle)?;
152
153 match handle {
154 Handle::Root => {
155 let total = total_display_count();
156 let mut out = format!("displays={}\n", total);
157 for i in 0..total {
158 if let Some((adapter, local_id)) = get_adapter_for_display(i) {
159 let (w, h) = adapter.display_size(local_id);
160 out.push_str(&format!("{}.0={}x{}\n", i, w, h));
161 }
162 }
163 out.push_str("damage\n");
164 out.push_str("clear\n");
165 let b = out.as_bytes();
166 let start = offset as usize;
167 if start >= b.len() {
168 return Ok(0);
169 }
170 let n = core::cmp::min(b.len() - start, buf.len());
171 buf[..n].copy_from_slice(&b[start..start + n]);
172 Ok(n)
173 }
174 Handle::Info => {
175 let total = total_display_count();
176 let mut s = format!("adapter_displays={}\n", total);
177 for i in 0..total {
178 if let Some((adapter, local_id)) = get_adapter_for_display(i) {
179 let (w, h) = adapter.display_size(local_id);
180 s.push_str(&format!("display.{}.{}x{}\n", i, w, h));
181 }
182 }
183 let b = s.as_bytes();
184 let start = offset as usize;
185 if start >= b.len() {
186 return Ok(0);
187 }
188 let n = core::cmp::min(b.len() - start, buf.len());
189 buf[..n].copy_from_slice(&b[start..start + n]);
190 Ok(n)
191 }
192 Handle::Screen(display_id, screen) => {
193 let scr = screen.read();
195 let scr_stride = scr.stride() as usize;
196 let start = offset as usize;
197 let total = (scr_stride * scr.height() as usize);
198 if start >= total {
199 return Ok(0);
200 }
201 let n = core::cmp::min(total - start, buf.len());
202 unsafe {
203 core::ptr::copy_nonoverlapping(scr.pixels().add(start), buf.as_mut_ptr(), n);
204 }
205 Ok(n)
206 }
207 Handle::Damage | Handle::Clear => Err(SyscallError::PermissionDenied),
208 }
209 }
210
211 fn write(&self, fid: u64, _offset: u64, buf: &[u8]) -> Result<usize, SyscallError> {
212 let h = self.handles.read();
213 let handle = h.get(&fid).ok_or(SyscallError::BadHandle)?;
214
215 match handle {
216 Handle::Root | Handle::Info => Err(SyscallError::PermissionDenied),
217 Handle::Screen(display_id, screen) => {
218 if buf.len() < 7 {
221 return Err(SyscallError::InvalidArgument);
222 }
223 let x = u16::from_le_bytes([buf[0], buf[1]]) as u32;
224 let y = u16::from_le_bytes([buf[2], buf[3]]) as u32;
225 let pixels = &buf[4..];
226
227 let mut scr = screen.write();
228 let bpp = scr.bpp() as usize;
229 let bpx = bpp / 8;
230 let stride = scr.stride() as usize;
231 let dst = scr.pixels_mut();
232
233 if bpp == 32 {
234 let mut off = 0;
235 let mut py = y;
236 while off + 3 < pixels.len() && py < scr.height() {
237 let mut px = x;
238 while off + 3 < pixels.len() && px < scr.width() {
239 let r = pixels[off];
240 let g = pixels[off + 1];
241 let b = pixels[off + 2];
242 off += 3;
243 let pixel = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
244 unsafe {
245 core::ptr::write_volatile(
246 dst.add(py as usize * stride + px as usize * 4) as *mut u32,
247 pixel,
248 );
249 }
250 px += 1;
251 }
252 py += 1;
253 }
254 } else if bpp == 24 {
255 let mut off = 0;
256 let mut py = y;
257 while off + 2 < pixels.len() && py < scr.height() {
258 let mut px = x;
259 while off + 2 < pixels.len() && px < scr.width() {
260 let r = pixels[off];
261 let g = pixels[off + 1];
262 let b = pixels[off + 2];
263 off += 3;
264 let ptr = unsafe { dst.add(py as usize * stride + px as usize * 3) };
265 unsafe {
266 core::ptr::write_volatile(ptr, r);
267 core::ptr::write_volatile(ptr.add(1), g);
268 core::ptr::write_volatile(ptr.add(2), b);
269 }
270 px += 1;
271 }
272 py += 1;
273 }
274 }
275 Ok(buf.len())
276 }
277 Handle::Damage => {
278 let s = core::str::from_utf8(buf).unwrap_or("").trim();
280 if s == "present" || s.is_empty() {
281 let total = total_display_count();
283 for i in 0..total {
284 if let Some((adapter, local_id)) = get_adapter_for_display(i) {
285 if let Some(screen_arc) = self.screens.read().get(&(i as u32)) {
286 let scr = screen_arc.read();
287 let damage = Damage::full(scr.width(), scr.height());
288 adapter.update_plane(local_id, &*scr, damage);
289 }
290 }
291 }
292 } else {
293 let parts: Vec<&str> = s.split(',').collect();
294 let (display_id, x, y, w, h) = if parts.len() == 5 {
295 let did: u32 = parts[0].parse().unwrap_or(0);
296 (
297 did,
298 parts[1].parse().unwrap_or(0),
299 parts[2].parse().unwrap_or(0),
300 parts[3].parse().unwrap_or(0),
301 parts[4].parse().unwrap_or(0),
302 )
303 } else if parts.len() == 4 {
304 (
305 0u32,
306 parts[0].parse().unwrap_or(0),
307 parts[1].parse().unwrap_or(0),
308 parts[2].parse().unwrap_or(0),
309 parts[3].parse().unwrap_or(0),
310 )
311 } else {
312 return Err(SyscallError::InvalidArgument);
313 };
314 if w > 0 && h > 0 {
315 if let Some((adapter, local_id)) =
316 get_adapter_for_display(display_id as usize)
317 {
318 if let Some(screen_arc) = self.screens.read().get(&display_id) {
319 let scr = screen_arc.read();
320 let damage = Damage {
321 x,
322 y,
323 width: w,
324 height: h,
325 };
326 adapter.update_plane(local_id, &*scr, damage);
327 }
328 }
329 }
330 }
331 Ok(buf.len())
332 }
333 Handle::Clear => {
334 let screens = self.screens.read();
336 for (_, screen_arc) in screens.iter() {
337 let mut scr = screen_arc.write();
338 unsafe {
339 core::ptr::write_bytes(
340 scr.pixels_mut(),
341 0,
342 scr.stride() as usize * scr.height() as usize,
343 );
344 }
345 }
346 let total = total_display_count();
348 for i in 0..total {
349 if let Some((adapter, local_id)) = get_adapter_for_display(i) {
350 let fb = adapter.create_framebuffer(
351 adapter.display_size(local_id).0,
352 adapter.display_size(local_id).1,
353 );
354 adapter.update_plane(local_id, &fb, Damage::full(fb.width(), fb.height()));
355 }
356 }
357 Ok(buf.len())
358 }
359 }
360 }
361
362 fn close(&self, fid: u64) -> Result<(), SyscallError> {
363 self.handles.write().remove(&fid);
364 Ok(())
365 }
366
367 fn stat(&self, fid: u64) -> Result<FileStat, SyscallError> {
368 let h = self.handles.read();
369 let handle = h.get(&fid).ok_or(SyscallError::BadHandle)?;
370
371 Ok(match handle {
372 Handle::Root => finalize_pseudo_stat(
373 FileStat {
374 st_ino: 0,
375 st_mode: 0o040555,
376 st_nlink: 2,
377 st_size: 0,
378 ..FileStat::zeroed()
379 },
380 DEV_CHAR_FS,
381 0,
382 ),
383 Handle::Info => finalize_pseudo_stat(
384 FileStat {
385 st_ino: 1,
386 st_mode: 0o0100444,
387 st_nlink: 1,
388 st_size: 256,
389 ..FileStat::zeroed()
390 },
391 DEV_CHAR_FS,
392 1,
393 ),
394 Handle::Screen(did, _) => finalize_pseudo_stat(
395 FileStat {
396 st_ino: 2 + *did as u64,
397 st_mode: 0o0100666,
398 st_nlink: 1,
399 st_size: 0,
400 ..FileStat::zeroed()
401 },
402 DEV_CHAR_FS,
403 2 + *did as u64,
404 ),
405 Handle::Damage => finalize_pseudo_stat(
406 FileStat {
407 st_ino: 0x100,
408 st_mode: 0o0100222,
409 st_nlink: 1,
410 st_size: 0,
411 ..FileStat::zeroed()
412 },
413 DEV_CHAR_FS,
414 0x100,
415 ),
416 Handle::Clear => finalize_pseudo_stat(
417 FileStat {
418 st_ino: 0x101,
419 st_mode: 0o0100222,
420 st_nlink: 1,
421 st_size: 0,
422 ..FileStat::zeroed()
423 },
424 DEV_CHAR_FS,
425 0x101,
426 ),
427 })
428 }
429
430 fn readdir(&self, fid: u64) -> Result<Vec<DirEntry>, SyscallError> {
431 let h = self.handles.read();
432 if !matches!(h.get(&fid), Some(Handle::Root)) {
433 return Err(SyscallError::InvalidArgument);
434 }
435
436 let total = total_display_count();
437 let mut entries = Vec::new();
438
439 entries.push(DirEntry {
440 ino: 1,
441 file_type: DT_REG,
442 name: String::from("info"),
443 });
444
445 for i in 0..total {
446 entries.push(DirEntry {
447 ino: 2 + i as u64,
448 file_type: DT_CHR,
449 name: format!("{}.0", i),
450 });
451 }
452
453 entries.push(DirEntry {
454 ino: 0x100,
455 file_type: DT_CHR,
456 name: String::from("damage"),
457 });
458 entries.push(DirEntry {
459 ino: 0x101,
460 file_type: DT_CHR,
461 name: String::from("clear"),
462 });
463
464 Ok(entries)
465 }
466}
467
468pub fn register_display_scheme() -> Result<(), SyscallError> {
470 if let Some(adapter) = SimpleDisplayAdapter::from_framebuffer() {
472 graphics_adapter::register_adapter(Arc::new(adapter));
473 }
474
475 let scheme = Arc::new(DisplayScheme::new());
476 crate::vfs::scheme_router::register_scheme("display", scheme)?;
477 crate::vfs::scheme_router::mount_scheme("display", "/dev/display")?;
478 log::info!(
479 "[display] Scheme at /dev/display/ ({} display(s))",
480 total_display_count()
481 );
482 Ok(())
483}
484
485use super::graphics_adapter;