From 31793e7c0c28d4b9ddaf82bdf77684f2a148491c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 15 May 2026 19:16:00 +0200 Subject: [PATCH] drivers/graphics/driver-graphics: Implement cursor planes --- .../driver-graphics/src/ioctl/cursor.rs | 93 ++++++++++++++----- .../graphics/driver-graphics/src/ioctl/mod.rs | 8 +- .../driver-graphics/src/kms/objects.rs | 20 +++- drivers/graphics/driver-graphics/src/lib.rs | 31 +------ drivers/graphics/ihdgd/src/device/mod.rs | 2 + drivers/graphics/ihdgd/src/device/scheme.rs | 10 +- drivers/graphics/vesad/src/scheme.rs | 10 +- drivers/graphics/virtio-gpud/src/scheme.rs | 85 +++++++++++------ 8 files changed, 156 insertions(+), 103 deletions(-) diff --git a/drivers/graphics/driver-graphics/src/ioctl/cursor.rs b/drivers/graphics/driver-graphics/src/ioctl/cursor.rs index ffb79a8a88..4399739ba3 100644 --- a/drivers/graphics/driver-graphics/src/ioctl/cursor.rs +++ b/drivers/graphics/driver-graphics/src/ioctl/cursor.rs @@ -1,18 +1,25 @@ use std::collections::HashMap; +use std::sync::atomic::AtomicBool; +use drm_fourcc::DrmFourcc; use drm_sys::{DRM_MODE_CURSOR_BO, DRM_MODE_CURSOR_MOVE}; -use syscall::{ENOENT, ENXIO, Error}; +use syscall::{EINVAL, ENXIO, Error}; -use crate::{DrmHandle, GraphicsAdapter, VtState}; +use crate::kms::objects::{KmsFramebuffer, KmsObjectId, KmsObjects, KmsRect}; +use crate::{Damage, DrmHandle, GraphicsAdapter, VtState}; pub(super) fn mode_cursor( adapter: &mut T, + objects: &mut KmsObjects, + active_vt: usize, vts: &mut HashMap>, handle: &mut DrmHandle, data: redox_ioctl::drm::DrmModeCursor<'_>, ) -> Result { cursor_inner( adapter, + objects, + active_vt, vts, handle, data.flags(), @@ -29,12 +36,16 @@ pub(super) fn mode_cursor( pub(super) fn mode_cursor2( adapter: &mut T, + objects: &mut KmsObjects, + active_vt: usize, vts: &mut HashMap>, handle: &mut DrmHandle, data: redox_ioctl::drm::DrmModeCursor2<'_>, ) -> Result { cursor_inner( adapter, + objects, + active_vt, vts, handle, data.flags(), @@ -51,44 +62,84 @@ pub(super) fn mode_cursor2( fn cursor_inner( adapter: &mut T, + objects: &mut KmsObjects, + active_vt: usize, vts: &mut HashMap>, handle: &mut DrmHandle, flags: u32, - _crtc_id: u32, + crtc_id: u32, x: i32, y: i32, - _width: u32, - _height: u32, + width: u32, + height: u32, handle_id: u32, hot_x: i32, hot_y: i32, ) -> Result { - let vt_state = vts.get_mut(&handle.vt).unwrap(); - - let Some(cursor_plane) = &mut vt_state.cursor_plane else { + let crtc_id = KmsObjectId(crtc_id); + let Some(plane) = objects.get_crtc(crtc_id)?.lock().unwrap().cursor_plane else { return Err(Error::new(ENXIO)); }; + let mut new_state = objects.get_plane(plane)?.lock().unwrap().state.clone(); + let old_fb_id = new_state.fb_id; + new_state.crtc_id = Some(crtc_id); - let update_buffer = flags & DRM_MODE_CURSOR_BO != 0; - if update_buffer { - cursor_plane.buffer = if handle_id == 0 { - None - } else if let Some(buffer) = handle.buffers.get(&handle_id) { - Some(buffer.clone()) + if flags & DRM_MODE_CURSOR_BO != 0 { + if handle_id == 0 { + new_state.fb_id = None; } else { - return Err(Error::new(ENOENT)); - }; - cursor_plane.hot_x = hot_x; - cursor_plane.hot_y = hot_y; + let buffer = handle.buffers.get(&handle_id).ok_or(Error::new(EINVAL))?; + let fb = adapter.create_framebuffer(buffer); + let fb_id = objects.add_framebuffer(KmsFramebuffer { + closed: AtomicBool::new(true), + width, + height, + pixel_format: DrmFourcc::Argb8888, + pitch: width * 4, + buffer: buffer.clone(), + driver_data: fb, + }); + + new_state.fb_id = Some(fb_id); + new_state.src_rect = KmsRect { + x: 0, + y: 0, + width, + height, + }; + new_state.crtc_rect.width = width; + new_state.crtc_rect.height = height; + if let Some(hotspot) = &mut new_state.hotspot { + *hotspot = (hot_x, hot_y); + } + } } if flags & DRM_MODE_CURSOR_MOVE != 0 { - cursor_plane.x = x; - cursor_plane.y = y; + new_state.crtc_rect.x = x; + new_state.crtc_rect.y = y; } - adapter.handle_cursor(cursor_plane, update_buffer); + let plane = objects.get_plane(plane).unwrap(); + + if handle.vt == active_vt { + #[rustfmt::skip] + let damage = if flags & DRM_MODE_CURSOR_BO != 0 { + Damage { x: 0, y: 0, width, height } + } else { + Damage { x: 0, y: 0, width: 0, height: 0 } + }; + adapter.set_plane(&objects, plane, new_state.clone(), damage)?; + } + vts.get_mut(&handle.vt).unwrap().plane_state[plane.lock().unwrap().plane_index as usize] = + new_state; + + if let Some(old_fb_id) = old_fb_id { + if !VtState::fb_has_any_use(vts, old_fb_id) { + objects.remove_framebuffer_if_closed(old_fb_id); + } + } Ok(0) } diff --git a/drivers/graphics/driver-graphics/src/ioctl/mod.rs b/drivers/graphics/driver-graphics/src/ioctl/mod.rs index 649c34d6e3..0b0d4067aa 100644 --- a/drivers/graphics/driver-graphics/src/ioctl/mod.rs +++ b/drivers/graphics/driver-graphics/src/ioctl/mod.rs @@ -202,7 +202,7 @@ pub(crate) fn call_ioctl( Ok(0) }), ipc::MODE_CURSOR => ipc::DrmModeCursor::with(payload, |data| { - cursor::mode_cursor(adapter, vts, handle, data) + cursor::mode_cursor(adapter, objects, active_vt, vts, handle, data) }), ipc::MODE_GET_ENCODER => ipc::DrmModeGetEncoder::with(payload, |mut data| { let encoder = objects.get_encoder(KmsObjectId(data.encoder_id()))?; @@ -345,8 +345,8 @@ pub(crate) fn call_ioctl( Damage { x: 0, y: 0, - width: 0, - height: 0, + width: data.src_w(), + height: data.src_h(), }, )?; } @@ -380,7 +380,7 @@ pub(crate) fn call_ioctl( property::mode_obj_get_properties(objects, data) }), ipc::MODE_CURSOR2 => ipc::DrmModeCursor2::with(payload, |data| { - cursor::mode_cursor2(adapter, vts, handle, data) + cursor::mode_cursor2(adapter, objects, active_vt, vts, handle, data) }), ipc::MODE_GET_FB2 => ipc::DrmModeFbCmd2::with(payload, |data| { framebuffer::mode_get_fb2(objects, handle, data) diff --git a/drivers/graphics/driver-graphics/src/kms/objects.rs b/drivers/graphics/driver-graphics/src/kms/objects.rs index ffd320800f..b999ceb4d5 100644 --- a/drivers/graphics/driver-graphics/src/kms/objects.rs +++ b/drivers/graphics/driver-graphics/src/kms/objects.rs @@ -88,8 +88,13 @@ impl KmsObjects { plane_data: T::Plane, plane_data_state: ::State, ) -> (KmsObjectId, KmsObjectId) { - let primary_plane = - self.add_plane(&[], KmsPlaneType::Primary, plane_data, plane_data_state); + let primary_plane = self.add_plane( + &[], + KmsPlaneType::Primary, + false, + plane_data, + plane_data_state, + ); let crtc_index = self.crtcs.len() as u32; let id = self.add(Mutex::new(KmsCrtc { @@ -97,6 +102,7 @@ impl KmsObjects { gamma_size: 0, properties: KmsCrtc::base_properties(), primary_plane, + cursor_plane: None, state: KmsCrtcState { mode: None, driver_data: driver_data_state, @@ -132,9 +138,14 @@ impl KmsObjects { &mut self, crtcs: &[KmsObjectId], plane_type: KmsPlaneType, + has_hotspot: bool, driver_data: T::Plane, driver_data_state: ::State, ) -> KmsObjectId { + if has_hotspot { + assert_eq!(plane_type, KmsPlaneType::Cursor); + } + let mut possible_crtcs = 0u32; for &crtc in crtcs { possible_crtcs |= 1 << self.get_crtc(crtc).unwrap().lock().unwrap().crtc_index @@ -160,6 +171,7 @@ impl KmsObjects { width: 0, height: 0, }, + hotspot: has_hotspot.then_some((0, 0)), driver_data: driver_data_state, }, driver_data, @@ -297,6 +309,7 @@ pub struct KmsCrtc { pub gamma_size: u32, pub properties: Vec>, pub primary_plane: KmsObjectId, + pub cursor_plane: Option, pub state: KmsCrtcState, pub driver_data: T::Crtc, } @@ -346,6 +359,7 @@ pub struct KmsPlaneState { pub crtc_id: Option, pub src_rect: KmsRect, pub crtc_rect: KmsRect, + pub hotspot: Option<(i32, i32)>, pub driver_data: ::State, } @@ -356,6 +370,7 @@ impl Clone for KmsPlaneState { crtc_id: self.crtc_id.clone(), src_rect: self.src_rect.clone(), crtc_rect: self.crtc_rect.clone(), + hotspot: self.hotspot, driver_data: self.driver_data.clone(), } } @@ -395,6 +410,7 @@ define_object_props!(object, KmsPlane { SRC_H { get => u64::from(object.state.src_rect.height), } + // FIXME HOTSPOT_X and HOTSPOT_Y if supported by graphics card }); #[derive(Copy, Clone, Debug, PartialEq)] diff --git a/drivers/graphics/driver-graphics/src/lib.rs b/drivers/graphics/driver-graphics/src/lib.rs index dddaf9c5e3..814a2ab94d 100644 --- a/drivers/graphics/driver-graphics/src/lib.rs +++ b/drivers/graphics/driver-graphics/src/lib.rs @@ -112,11 +112,6 @@ pub trait GraphicsAdapter: Sized + Debug { new_plane_state: KmsPlaneState, damage: Damage, ) -> syscall::Result<()>; - - // FIXME replace this with an actual plane marked as cursor plane once we - // support planes internally. - fn has_cursor_plane(&self) -> bool; - fn handle_cursor(&mut self, cursor: &CursorPlane, dirty_fb: bool); } pub trait Buffer: Debug { @@ -127,14 +122,6 @@ pub trait Framebuffer: Debug {} impl Framebuffer for () {} -pub struct CursorPlane { - pub x: i32, - pub y: i32, - pub hot_x: i32, - pub hot_y: i32, - pub buffer: Option>, -} - pub struct GraphicsScheme { inner: GraphicsSchemeInner, inputd_handle: DisplayHandle, @@ -281,7 +268,6 @@ struct VtState { connector_state: Vec>, crtc_state: Vec>, plane_state: Vec>, - cursor_plane: Option>, } impl VtState { @@ -313,7 +299,6 @@ struct DrmHandle { impl GraphicsSchemeInner { fn get_or_create_vt<'a>( - adapter: &T, objects: &KmsObjects, vts: &'a mut HashMap>, vt: usize, @@ -331,13 +316,6 @@ impl GraphicsSchemeInner { .planes() .map(|plane| plane.lock().unwrap().state.clone()) .collect(), - cursor_plane: adapter.has_cursor_plane().then(|| CursorPlane { - x: 0, - y: 0, - hot_x: 0, - hot_y: 0, - buffer: None, - }), }) } @@ -354,8 +332,7 @@ impl GraphicsSchemeInner { self.active_vt = vt; - let vt_state = - GraphicsSchemeInner::get_or_create_vt(&self.adapter, &self.objects, &mut self.vts, vt); + let vt_state = GraphicsSchemeInner::get_or_create_vt(&self.objects, &mut self.vts, vt); for (connector_idx, connector_state) in vt_state.connector_state.iter().enumerate() { let connector_id = self.objects.connector_ids()[connector_idx]; @@ -410,10 +387,6 @@ impl GraphicsSchemeInner { ) .unwrap(); } - - if let Some(cursor_plane) = &vt_state.cursor_plane { - self.adapter.handle_cursor(cursor_plane, true); - } } } @@ -447,7 +420,7 @@ impl SchemeSync for GraphicsSchemeInner { .map_err(|_| Error::new(EINVAL))?; // Ensure the VT exists such that the rest of the methods can freely access it. - Self::get_or_create_vt(&self.adapter, &self.objects, &mut self.vts, vt); + Self::get_or_create_vt(&self.objects, &mut self.vts, vt); Handle::V2(DrmHandle { vt, diff --git a/drivers/graphics/ihdgd/src/device/mod.rs b/drivers/graphics/ihdgd/src/device/mod.rs index 941d19f4bb..244dbb49d8 100644 --- a/drivers/graphics/ihdgd/src/device/mod.rs +++ b/drivers/graphics/ihdgd/src/device/mod.rs @@ -460,6 +460,8 @@ impl Device { (), ); + // FIXME add cursor plane + let (width, height) = (fb.width, fb.height); let fb_id = objects.add_framebuffer(fb); diff --git a/drivers/graphics/ihdgd/src/device/scheme.rs b/drivers/graphics/ihdgd/src/device/scheme.rs index ad4fe2b296..6917317118 100644 --- a/drivers/graphics/ihdgd/src/device/scheme.rs +++ b/drivers/graphics/ihdgd/src/device/scheme.rs @@ -5,7 +5,7 @@ use driver_graphics::kms::objects::{ KmsCrtc, KmsCrtcDriver, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneDriver, KmsPlaneState, }; -use driver_graphics::{Buffer, CursorPlane, Damage, GraphicsAdapter}; +use driver_graphics::{Buffer, Damage, GraphicsAdapter}; use drm_sys::{ DRM_CAP_DUMB_BUFFER, DRM_CAP_DUMB_PREFERRED_DEPTH, DRM_CAP_DUMB_PREFER_SHADOW, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT, @@ -135,12 +135,4 @@ impl GraphicsAdapter for Device { Ok(()) } - - fn has_cursor_plane(&self) -> bool { - false - } - - fn handle_cursor(&mut self, _cursor: &CursorPlane, _dirty_fb: bool) { - unimplemented!("ihdgd does not support this function"); - } } diff --git a/drivers/graphics/vesad/src/scheme.rs b/drivers/graphics/vesad/src/scheme.rs index 56d5d7370e..4720ab72fe 100644 --- a/drivers/graphics/vesad/src/scheme.rs +++ b/drivers/graphics/vesad/src/scheme.rs @@ -7,7 +7,7 @@ use driver_graphics::kms::connector::{KmsConnectorDriver, KmsConnectorStatus}; use driver_graphics::kms::objects::{ KmsCrtc, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneState, }; -use driver_graphics::{Buffer, CursorPlane, Damage, GraphicsAdapter}; +use driver_graphics::{Buffer, Damage, GraphicsAdapter}; use drm_sys::{ DRM_CAP_DUMB_BUFFER, DRM_CAP_DUMB_PREFERRED_DEPTH, DRM_CAP_DUMB_PREFER_SHADOW, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT, @@ -163,14 +163,6 @@ impl GraphicsAdapter for FbAdapter { Ok(()) } - - fn has_cursor_plane(&self) -> bool { - false - } - - fn handle_cursor(&mut self, _cursor: &CursorPlane, _dirty_fb: bool) { - unimplemented!("Vesad does not support this function"); - } } #[derive(Debug)] diff --git a/drivers/graphics/virtio-gpud/src/scheme.rs b/drivers/graphics/virtio-gpud/src/scheme.rs index fd83dc473e..b1d81703b0 100644 --- a/drivers/graphics/virtio-gpud/src/scheme.rs +++ b/drivers/graphics/virtio-gpud/src/scheme.rs @@ -4,9 +4,10 @@ use std::sync::{Arc, Mutex}; use common::{dma::Dma, sgl}; use driver_graphics::kms::connector::{KmsConnectorDriver, KmsConnectorStatus}; use driver_graphics::kms::objects::{ - KmsCrtc, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneState, + KmsCrtc, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneDriver, KmsPlaneState, + KmsPlaneType, }; -use driver_graphics::{Buffer as DrmBuffer, CursorPlane, Damage, GraphicsAdapter, GraphicsScheme}; +use driver_graphics::{Buffer as DrmBuffer, Damage, GraphicsAdapter, GraphicsScheme}; use drm_sys::{ DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH, DRM_CAP_DUMB_BUFFER, DRM_CAP_DUMB_PREFERRED_DEPTH, DRM_CAP_DUMB_PREFER_SHADOW, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT, @@ -39,6 +40,15 @@ impl KmsConnectorDriver for VirtGpuConnector { type State = (); } +#[derive(Debug)] +pub struct VirtGpuPlane { + is_cursor: bool, +} + +impl KmsPlaneDriver for VirtGpuPlane { + type State = (); +} + pub struct VirtGpuFramebuffer<'a> { queue: Arc>, id: ResourceId, @@ -343,7 +353,7 @@ impl<'a> VirtGpuAdapter<'a> { impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { type Connector = VirtGpuConnector; type Crtc = (); - type Plane = (); + type Plane = VirtGpuPlane; type Buffer = VirtGpuFramebuffer<'a>; type Framebuffer = (); @@ -362,7 +372,18 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { }); for display_id in 0..self.config.num_scanouts.get() { - let (crtc, _primary_plane_id) = objects.add_crtc((), (), (), ()); + let (crtc, _primary_plane_id) = + objects.add_crtc((), (), VirtGpuPlane { is_cursor: false }, ()); + + let cursor_plane = objects.add_plane( + &[crtc], + KmsPlaneType::Cursor, + true, + VirtGpuPlane { is_cursor: true }, + (), + ); + + objects.get_crtc(crtc).unwrap().lock().unwrap().cursor_plane = Some(cursor_plane); objects.add_connector(VirtGpuConnector { display_id }, (), &[crtc]); } @@ -444,10 +465,6 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { damage: Damage, ) -> syscall::Result<()> { futures::executor::block_on(async { - let Some(crtc_id) = new_plane_state.crtc_id else { - return Ok(()); - }; - let crtc = objects.get_crtc(crtc_id).unwrap().lock().unwrap(); let mut plane = plane.lock().unwrap(); let framebuffer = new_plane_state @@ -455,6 +472,37 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { .map(|fb_id| objects.get_framebuffer_maybe_closed(fb_id)) .transpose()?; + if plane.driver_data.is_cursor { + if let Some(framebuffer) = framebuffer { + if damage.width != 0 || damage.height != 0 { + self.update_cursor( + &framebuffer.buffer, + new_plane_state.crtc_rect.x, + new_plane_state.crtc_rect.y, + new_plane_state.hotspot.unwrap().0, + new_plane_state.hotspot.unwrap().1, + ) + .await; + } else { + self.move_cursor(new_plane_state.crtc_rect.x, new_plane_state.crtc_rect.y) + .await; + } + } else { + if plane.state.fb_id.is_some() { + self.disable_cursor().await; + } + } + + plane.state = new_plane_state; + + return Ok(()); + } + + let Some(crtc_id) = new_plane_state.crtc_id else { + return Ok(()); + }; + let crtc = objects.get_crtc(crtc_id).unwrap().lock().unwrap(); + plane.state = new_plane_state; for connector in objects.connectors() { @@ -519,27 +567,6 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { Ok(()) }) } - - fn has_cursor_plane(&self) -> bool { - true - } - - fn handle_cursor(&mut self, cursor: &CursorPlane, dirty_fb: bool) { - futures::executor::block_on(async { - if let Some(buffer) = &cursor.buffer { - if dirty_fb { - self.update_cursor(buffer, cursor.x, cursor.y, cursor.hot_x, cursor.hot_y) - .await; - } else { - self.move_cursor(cursor.x, cursor.y).await; - } - } else { - if dirty_fb { - self.disable_cursor().await; - } - } - }); - } } pub struct GpuScheme {}