use smithay::{
backend::renderer::{Color32F, Frame},
input::pointer::{CursorImageStatus, CursorImageSurfaceData},
reexports::wayland_server::protocol::wl_surface::WlSurface,
utils::{Physical, Rectangle},
wayland::compositor::{SurfaceAttributes, get_parent, with_states},
};
use super::cursor_theme::SoftwareCursorSprite;
use super::draw_primitives::draw_rect;
pub(crate) fn cursor_surface_hotspot(surface: &WlSurface) -> (i32, i32) {
with_states(surface, |states| {
states
.data_map
.get::<CursorImageSurfaceData>()
.and_then(|attrs| {
attrs
.lock()
.ok()
.map(|attr| (attr.hotspot.x, attr.hotspot.y))
})
.unwrap_or((0, 0))
})
}
pub(crate) fn handle_cursor_surface_commit(
cursor_image: &CursorImageStatus,
surface: &WlSurface,
) -> bool {
let CursorImageStatus::Surface(cursor_surface) = cursor_image else {
return false;
};
let root = surface_tree_root(surface);
if cursor_surface != &root {
return false;
}
if surface == &root {
with_states(surface, |states| {
let cursor_image_attributes = states.data_map.get::<CursorImageSurfaceData>();
if let Some(mut cursor_image_attributes) =
cursor_image_attributes.map(|attrs| attrs.lock().unwrap())
{
let buffer_delta = states
.cached_state
.get::<SurfaceAttributes>()
.current()
.buffer_delta
.take();
if let Some(buffer_delta) = buffer_delta {
cursor_image_attributes.hotspot -= buffer_delta;
}
}
});
}
true
}
fn surface_tree_root(surface: &WlSurface) -> WlSurface {
let mut root = surface.clone();
while let Some(parent) = get_parent(&root) {
root = parent;
}
root
}
pub(crate) fn draw_cursor_sprite<F: Frame>(
frame: &mut F,
damage: Rectangle<i32, Physical>,
cursor_screen: (f32, f32),
sprite: &SoftwareCursorSprite,
) -> Result<(), F::Error> {
let (sx, sy) = cursor_screen;
let x0 = sx.round() as i32 - sprite.hotspot_x;
let y0 = sy.round() as i32 - sprite.hotspot_y;
let w = sprite.width;
let h = sprite.height;
for y in 0..h {
let mut x = 0usize;
while x < w {
let base = (y * w + x) * 4;
let a = sprite.pixels_bgra[base + 3];
if a == 0 {
x += 1;
continue;
}
let b = sprite.pixels_bgra[base];
let g = sprite.pixels_bgra[base + 1];
let r = sprite.pixels_bgra[base + 2];
let mut run_end = x + 1;
while run_end < w {
let i = (y * w + run_end) * 4;
if sprite.pixels_bgra[i] != b
|| sprite.pixels_bgra[i + 1] != g
|| sprite.pixels_bgra[i + 2] != r
|| sprite.pixels_bgra[i + 3] != a
{
break;
}
run_end += 1;
}
draw_rect(
frame,
x0 + x as i32,
y0 + y as i32,
(run_end - x) as i32,
1,
Color32F::new(
r as f32 / 255.0,
g as f32 / 255.0,
b as f32 / 255.0,
a as f32 / 255.0,
),
damage,
)?;
x = run_end;
}
}
Ok(())
}