From 511c6080caf00ac1c115c68e49d3ef956c9de41f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 11 Mar 2026 22:49:19 +0100 Subject: [PATCH 1/2] drivers/graphics: Add framebuffer indirection to the DRM interface This matches the Linux DRM interface. With this I believe the only divergence from the Linux DRM interface that would be a breaking change to the in-tree users of the graphics api to change to the proper interface is the use of the UPDATE_PLANE ioctl in the place of proper modesetting interface. It should be possible to support this in parallel to UPDATE_PLANE, so updating Orbital to the DRM interface should now be possible. Orbital can be updated to the proper modesetting interface once it is implemented. --- drivers/graphics/console-draw/src/lib.rs | 18 +++++++++++------- drivers/graphics/driver-graphics/src/lib.rs | 4 ++++ drivers/graphics/fbbootlogd/src/scheme.rs | 14 +++++++------- drivers/graphics/fbcond/src/display.rs | 10 +++++----- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/drivers/graphics/console-draw/src/lib.rs b/drivers/graphics/console-draw/src/lib.rs index 20be8bb8d2..25a409042e 100644 --- a/drivers/graphics/console-draw/src/lib.rs +++ b/drivers/graphics/console-draw/src/lib.rs @@ -6,27 +6,31 @@ use std::{cmp, io, mem, ptr}; use drm::buffer::{Buffer, DrmFourcc}; use drm::control::dumbbuffer::{DumbBuffer, DumbMapping}; -use drm::control::Device; +use drm::control::{framebuffer, Device}; use graphics_ipc::v1::Damage; use graphics_ipc::v2::V2GraphicsHandle; use orbclient::FONT; pub struct V2DisplayMap { pub display_handle: V2GraphicsHandle, - pub fb: DumbBuffer, + pub fb: framebuffer::Handle, + pub buffer: DumbBuffer, mapping: DumbMapping<'static>, } impl V2DisplayMap { pub fn new(display_handle: V2GraphicsHandle, width: u32, height: u32) -> io::Result { - let mut fb = display_handle.create_dumb_buffer((width, height), DrmFourcc::Argb8888, 32)?; + let mut buffer = + display_handle.create_dumb_buffer((width, height), DrmFourcc::Argb8888, 32)?; + let fb = display_handle.add_framebuffer(&buffer, 24, 32)?; - let map = display_handle.map_dumb_buffer(&mut fb)?; + let map = display_handle.map_dumb_buffer(&mut buffer)?; let map = unsafe { mem::transmute::, DumbMapping<'static>>(map) }; Ok(Self { display_handle, fb, + buffer, mapping: map, }) } @@ -37,8 +41,8 @@ impl V2DisplayMap { self.mapping.as_mut_ptr() as *mut u32, self.mapping.len() / 4, ), - width: self.fb.size().0 as usize, - height: self.fb.size().1 as usize, + width: self.buffer.size().0 as usize, + height: self.buffer.size().1 as usize, } } } @@ -324,7 +328,7 @@ impl TextScreen { } } - let old_fb = mem::replace(&mut map.fb, new_fb); + let old_fb = mem::replace(&mut map.buffer, new_fb); map.mapping = new_mapping; let _ = map.display_handle.destroy_dumb_buffer(old_fb); diff --git a/drivers/graphics/driver-graphics/src/lib.rs b/drivers/graphics/driver-graphics/src/lib.rs index 01b9f7b2cb..e43c4cbc95 100644 --- a/drivers/graphics/driver-graphics/src/lib.rs +++ b/drivers/graphics/driver-graphics/src/lib.rs @@ -810,6 +810,10 @@ impl SchemeSync for GraphicsSchemeInner { data.set_handle(fb_handle_id(i)); Ok(0) }), + ipc::MODE_ADD_FB => ipc::DrmModeFbCmd::with(payload, |mut data| { + data.set_fb_id(fb_handle_id(id_index(data.handle()))); + Ok(0) + }), ipc::MODE_CREATE_DUMB => ipc::DrmModeCreateDumb::with(payload, |mut data| { if data.bpp() != 32 { return Err(Error::new(EINVAL)); diff --git a/drivers/graphics/fbbootlogd/src/scheme.rs b/drivers/graphics/fbbootlogd/src/scheme.rs index a599893aa1..f3485c94d1 100644 --- a/drivers/graphics/fbbootlogd/src/scheme.rs +++ b/drivers/graphics/fbbootlogd/src/scheme.rs @@ -122,7 +122,7 @@ impl FbbootlogScheme { self.is_scrollback = true; self.scrollback_offset = cmp::min( self.scrollback_offset, - buffer_len - map.fb.size().1 as usize / 16 + spare_lines, + buffer_len - map.buffer.size().1 as usize / 16 + spare_lines, ); let mut i = self.scrollback_offset; self.text_screen @@ -135,9 +135,9 @@ impl FbbootlogScheme { .write(map, &self.text_buffer.lines[i][..], &mut VecDeque::new()); i += 1; let yd = (damage.y + damage.height) as usize; - if i == buffer_len || yd + spare_lines * 16 > map.fb.size().1 as usize { + if i == buffer_len || yd + spare_lines * 16 > map.buffer.size().1 as usize { // render until end of screen - damage.height = map.fb.size().1 - damage.y; + damage.height = map.buffer.size().1 - damage.y; total_damage = total_damage.merge(damage); self.is_scrollback = i < buffer_len; break; @@ -146,7 +146,7 @@ impl FbbootlogScheme { } } map.display_handle - .update_plane(0, u32::from(map.fb.handle()), total_damage) + .update_plane(0, u32::from(map.buffer.handle()), total_damage) .unwrap(); } @@ -157,11 +157,11 @@ impl FbbootlogScheme { Ok((width, height)) => (width.into(), height.into()), Err(err) => { eprintln!("fbbootlogd: failed to get display size: {}", err); - map.fb.size() + map.buffer.size() } }; - if (width, height) != map.fb.size() { + if (width, height) != map.buffer.size() { match text_screen.resize(map, width, height) { Ok(()) => eprintln!("fbbootlogd: mapped display"), Err(err) => { @@ -248,7 +248,7 @@ impl SchemeSync for FbbootlogScheme { if let Some(map) = &self.display_map { map.display_handle - .update_plane(0, u32::from(map.fb.handle()), damage) + .update_plane(0, u32::from(map.buffer.handle()), damage) .unwrap(); } } diff --git a/drivers/graphics/fbcond/src/display.rs b/drivers/graphics/fbcond/src/display.rs index 2cc3b57d30..5bbd47c4a2 100644 --- a/drivers/graphics/fbcond/src/display.rs +++ b/drivers/graphics/fbcond/src/display.rs @@ -45,8 +45,8 @@ impl Display { Ok(map) => { log::debug!( "fbcond: Mapped new display with size {}x{}", - map.fb.size().0, - map.fb.size().1, + map.buffer.size().0, + map.buffer.size().1, ); self.map = Some(map) } @@ -64,11 +64,11 @@ impl Display { Ok((width, height)) => (width.into(), height.into()), Err(err) => { log::error!("fbcond: failed to get display size: {}", err); - map.fb.size() + map.buffer.size() } }; - if (width, height) != map.fb.size() { + if (width, height) != map.buffer.size() { match text_screen.resize(map, width, height) { Ok(()) => eprintln!("fbcond: mapped display"), Err(err) => { @@ -82,7 +82,7 @@ impl Display { pub fn sync_rect(&mut self, damage: Damage) { if let Some(map) = &self.map { map.display_handle - .update_plane(0, u32::from(map.fb.handle()), damage) + .update_plane(0, u32::from(map.buffer.handle()), damage) .unwrap(); } } From f01d0fe8a18312c6ef8234d7fd5f20fa732cc397 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 11 Mar 2026 23:17:38 +0100 Subject: [PATCH 2/2] drivers/graphics: Introduce UPDATE_CURSOR ioctl Orbital needs this as we don't implement cursor planes yet. --- drivers/graphics/driver-graphics/src/lib.rs | 132 ++++++++++++-------- drivers/graphics/graphics-ipc/src/v1.rs | 14 +-- drivers/graphics/graphics-ipc/src/v2.rs | 16 ++- 3 files changed, 99 insertions(+), 63 deletions(-) diff --git a/drivers/graphics/driver-graphics/src/lib.rs b/drivers/graphics/driver-graphics/src/lib.rs index e43c4cbc95..bcd150d510 100644 --- a/drivers/graphics/driver-graphics/src/lib.rs +++ b/drivers/graphics/driver-graphics/src/lib.rs @@ -349,6 +349,67 @@ impl GraphicsSchemeInner { }, ); } + + fn handle_cursor_update( + &mut self, + vt: usize, + cursor_damage: &graphics_ipc::v2::ipc::UpdateCursor, + ) -> Result<()> { + let vt_state = self.vts.get_mut(&vt).unwrap(); + + let Some(cursor_plane) = &mut vt_state.cursor_plane else { + // Hardware cursor not supported + return Err(Error::new(EINVAL)); + }; + + cursor_plane.x = cursor_damage.x; + cursor_plane.y = cursor_damage.y; + + if cursor_damage.header == 0 { + if vt != self.active_vt { + return Ok(()); + } + + self.adapter.handle_cursor(cursor_plane, false); + } else { + cursor_plane.hot_x = cursor_damage.hot_x; + cursor_plane.hot_y = cursor_damage.hot_y; + + let w: i32 = cursor_damage.width; + let h: i32 = cursor_damage.height; + let cursor_image = cursor_damage.cursor_img_bytes; + let cursor_ptr = self + .adapter + .map_cursor_framebuffer(&cursor_plane.framebuffer); + + //Clear previous image from backing storage + unsafe { + core::ptr::write_bytes(cursor_ptr as *mut u8, 0, 64 * 64 * 4); + } + + //Write image to backing storage + for row in 0..h { + let start: usize = (w * row) as usize; + let end: usize = (w * row + w) as usize; + + unsafe { + core::ptr::copy_nonoverlapping( + cursor_image[start..end].as_ptr(), + cursor_ptr.cast::().offset(64 * row as isize), + w as usize, + ); + } + } + + if vt != self.active_vt { + return Ok(()); + } + + self.adapter.handle_cursor(cursor_plane, true); + } + + return Ok(()); + } } const MAP_FAKE_OFFSET_MULTIPLIER: usize = 0x10_000_000; @@ -495,6 +556,14 @@ impl SchemeSync for GraphicsSchemeInner { ) -> Result { match self.handles.get(&id).ok_or(Error::new(EBADF))? { Handle::V1Screen { vt, screen } => { + if size_of_val(buf) == std::mem::size_of::() { + let cursor_damage = unsafe { &*buf.as_ptr().cast::() }; + + self.handle_cursor_update(*vt, cursor_damage)?; + + return Ok(buf.len()); + } + if *vt != self.active_vt { // This is a protection against background VT's spamming us with flush requests. We will // flush the framebuffer on the next VT switch anyway @@ -503,55 +572,6 @@ impl SchemeSync for GraphicsSchemeInner { let vt_state = self.vts.get_mut(vt).unwrap(); - if size_of_val(buf) == std::mem::size_of::() { - let Some(cursor_plane) = &mut vt_state.cursor_plane else { - // Hardware cursor not supported - return Err(Error::new(EINVAL)); - }; - - let cursor_damage = unsafe { *buf.as_ptr().cast::() }; - - cursor_plane.x = cursor_damage.x; - cursor_plane.y = cursor_damage.y; - - if cursor_damage.header == 0 { - self.adapter.handle_cursor(cursor_plane, false); - } else { - cursor_plane.hot_x = cursor_damage.hot_x; - cursor_plane.hot_y = cursor_damage.hot_y; - - let w: i32 = cursor_damage.width; - let h: i32 = cursor_damage.height; - let cursor_image = cursor_damage.cursor_img_bytes; - let cursor_ptr = self - .adapter - .map_cursor_framebuffer(&cursor_plane.framebuffer); - - //Clear previous image from backing storage - unsafe { - core::ptr::write_bytes(cursor_ptr as *mut u8, 0, 64 * 64 * 4); - } - - //Write image to backing storage - for row in 0..h { - let start: usize = (w * row) as usize; - let end: usize = (w * row + w) as usize; - - unsafe { - core::ptr::copy_nonoverlapping( - cursor_image[start..end].as_ptr(), - cursor_ptr.cast::().offset(64 * row as isize), - w as usize, - ); - } - } - - self.adapter.handle_cursor(cursor_plane, true); - } - - return Ok(buf.len()); - } - assert_eq!(buf.len(), std::mem::size_of::()); let damage = unsafe { *buf.as_ptr().cast::() }; @@ -935,6 +955,20 @@ impl SchemeSync for GraphicsSchemeInner { Ok(size_of::()) } + ipc::UPDATE_CURSOR => { + if payload.len() < size_of::() { + return Err(Error::new(EINVAL)); + } + let payload = unsafe { + transmute::<&mut [u8; size_of::()], &mut ipc::UpdateCursor>( + payload.as_mut_array().unwrap(), + ) + }; + let vt = *vt; + self.handle_cursor_update(vt, payload)?; + + Ok(size_of::()) + } _ => return Err(Error::new(EINVAL)), }, } diff --git a/drivers/graphics/graphics-ipc/src/v1.rs b/drivers/graphics/graphics-ipc/src/v1.rs index df882024c2..fbf2227268 100644 --- a/drivers/graphics/graphics-ipc/src/v1.rs +++ b/drivers/graphics/graphics-ipc/src/v1.rs @@ -1,14 +1,2 @@ pub use crate::common::Damage; - -#[derive(Debug, Copy, Clone)] -#[repr(C, packed)] -pub struct CursorDamage { - pub header: u32, - pub x: i32, - pub y: i32, - pub hot_x: i32, - pub hot_y: i32, - pub width: i32, - pub height: i32, - pub cursor_img_bytes: [u32; 4096], -} +pub use crate::v2::ipc::UpdateCursor as CursorDamage; diff --git a/drivers/graphics/graphics-ipc/src/v2.rs b/drivers/graphics/graphics-ipc/src/v2.rs index 7e132351b3..31b098f0bf 100644 --- a/drivers/graphics/graphics-ipc/src/v2.rs +++ b/drivers/graphics/graphics-ipc/src/v2.rs @@ -69,11 +69,25 @@ pub mod ipc { pub use redox_ioctl::drm::*; // FIXME replace these with proper drm interfaces - pub const UPDATE_PLANE: u64 = 6; + pub const UPDATE_PLANE: u64 = 0x12345670; #[repr(C, packed)] pub struct UpdatePlane { pub display_id: usize, pub fb_id: u32, pub damage: Damage, } + + pub const UPDATE_CURSOR: u64 = 0x12345671; + #[derive(Debug, Copy, Clone)] + #[repr(C, packed)] + pub struct UpdateCursor { + pub header: u32, + pub x: i32, + pub y: i32, + pub hot_x: i32, + pub hot_y: i32, + pub width: i32, + pub height: i32, + pub cursor_img_bytes: [u32; 4096], + } }