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