feat(compositor): render pointer cursor from CursorImageStatus::Surface

When a Wayland client calls wl_pointer.set_cursor, render the cursor
surface at pointer_location using render_elements_from_surface_tree.

Changes in render_output:
- Collect output_geo, pointer_location, cursor_status before the inner
 rendering block (avoids borrow conflict with space+drm destructure)
- Build cursor elements as Vec<SpaceRenderElements<_, WaylandSurfaceRenderElement<_>>>
 via render_elements_from_surface_tree with Kind::Cursor; hotspot is
 read from CursorImageSurfaceData on the cursor wl_surface
- Cursor elements prepend space elements (highest z-index first, matching
 render_elements_for_output sort order descending by z_index)
- CursorImageStatus::Hidden / Named: no cursor element emitted

New imports: SpaceRenderElements, CursorImageStatus, CursorImageSurfaceData,
render_elements_from_surface_tree, Kind, Scale, with_states

Hardware cursor plane deferred requires DRM cursor plane API audit.
This commit is contained in:
Marco Allegretti 2026-03-11 00:45:34 +01:00
parent fc5ada2079
commit 43269c9be1

View file

@ -33,13 +33,18 @@ use smithay::{
input::InputEvent, input::InputEvent,
libinput::{LibinputInputBackend, LibinputSessionInterface}, libinput::{LibinputInputBackend, LibinputSessionInterface},
renderer::{ renderer::{
element::surface::WaylandSurfaceRenderElement, element::{
Kind,
surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
},
gles::GlesRenderer, gles::GlesRenderer,
multigpu::{GpuManager, MultiRenderer, gbm::GbmGlesBackend}, multigpu::{GpuManager, MultiRenderer, gbm::GbmGlesBackend},
}, },
session::{Event as SessionEvent, Session, libseat::LibSeatSession}, session::{Event as SessionEvent, Session, libseat::LibSeatSession},
udev::{UdevBackend, UdevEvent, all_gpus, primary_gpu}, udev::{UdevBackend, UdevEvent, all_gpus, primary_gpu},
}, },
desktop::space::SpaceRenderElements,
input::pointer::{CursorImageStatus, CursorImageSurfaceData},
output::{Mode as WlMode, Output, PhysicalProperties, Subpixel}, output::{Mode as WlMode, Output, PhysicalProperties, Subpixel},
reexports::{ reexports::{
calloop::{EventLoop, Interest, Mode, PostAction, generic::Generic}, calloop::{EventLoop, Interest, Mode, PostAction, generic::Generic},
@ -48,8 +53,8 @@ use smithay::{
rustix::fs::OFlags, rustix::fs::OFlags,
wayland_server::Display, wayland_server::Display,
}, },
utils::{DeviceFd, Transform}, utils::{DeviceFd, Scale, Transform},
wayland::socket::ListeningSocketSource, wayland::{compositor::with_states, socket::ListeningSocketSource},
}; };
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -594,6 +599,10 @@ fn render_output(state: &mut WeftCompositorState, node: DrmNode, crtc: crtc::Han
.map(|d| d.start_time.elapsed()) .map(|d| d.start_time.elapsed())
.unwrap_or_default(); .unwrap_or_default();
let output_geo = state.space.output_geometry(&output).unwrap_or_default();
let pointer_location = state.pointer_location;
let cursor_status = state.cursor_image_status.clone();
{ {
let WeftCompositorState { let WeftCompositorState {
ref mut drm, ref mut drm,
@ -629,9 +638,36 @@ fn render_output(state: &mut WeftCompositorState, node: DrmNode, crtc: crtc::Han
} }
}; };
let elements = space let output_scale = output.current_scale().fractional_scale();
let mut elements: Vec<SpaceRenderElements<_, WaylandSurfaceRenderElement<_>>> =
if let CursorImageStatus::Surface(ref cursor_surface) = cursor_status {
let hotspot = with_states(cursor_surface, |states| {
states
.data_map
.get::<CursorImageSurfaceData>()
.and_then(|d| d.lock().ok().map(|g| g.hotspot))
.unwrap_or_default()
});
let cursor_pos = (pointer_location - output_geo.loc.to_f64() - hotspot.to_f64())
.to_physical_precise_round(output_scale);
render_elements_from_surface_tree(
&mut renderer,
cursor_surface,
cursor_pos,
Scale::from(output_scale),
1.0,
Kind::Cursor,
)
} else {
Vec::new()
};
elements.extend(
space
.render_elements_for_output(&mut renderer, &output, 1.0) .render_elements_for_output(&mut renderer, &output, 1.0)
.unwrap_or_default(); .unwrap_or_default(),
);
match surface.drm_output.render_frame( match surface.drm_output.render_frame(
&mut renderer, &mut renderer,