smithay/anvil/src/drawing.rs

284 lines
9.7 KiB
Rust
Raw Normal View History

2021-04-28 22:31:49 +00:00
#![allow(clippy::too_many_arguments)]
use std::cell::RefCell;
use slog::Logger;
use smithay::{
2021-05-13 17:59:47 +00:00
backend::{
2021-05-13 21:33:22 +00:00
renderer::{Frame, Renderer, Texture, Transform, ImportShm, ImportDma, BufferType, buffer_type},
2021-05-13 17:59:47 +00:00
SwapBuffersError,
},
reexports::wayland_server::protocol::{wl_buffer, wl_surface},
utils::Rectangle,
wayland::{
2021-05-26 17:12:45 +00:00
compositor::{roles::Role, Damage, SubsurfaceRole, TraversalAction},
data_device::DnDIconRole,
seat::CursorImageRole,
},
};
#[cfg(feature = "egl")]
use smithay::backend::{
egl::display::EGLBufferReader,
2021-05-13 21:33:22 +00:00
renderer::ImportEgl,
};
// hacky...
#[cfg(not(feature = "egl"))]
pub trait ImportEgl {}
#[cfg(not(feature = "egl"))]
impl<T> ImportEgl for T {}
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
2021-05-13 17:59:47 +00:00
struct BufferTextures<T> {
buffer: Option<wl_buffer::WlBuffer>,
2021-05-13 17:59:47 +00:00
texture: T,
}
impl<T> Drop for BufferTextures<T> {
fn drop(&mut self) {
if let Some(buffer) = self.buffer.take() {
buffer.release();
}
2021-05-13 17:59:47 +00:00
}
}
2021-05-23 21:03:43 +00:00
pub fn draw_cursor<R, E, F, T>(
renderer: &mut R,
2021-05-23 21:03:43 +00:00
frame: &mut F,
surface: &wl_surface::WlSurface,
#[cfg(feature = "egl")]
2021-05-13 17:59:47 +00:00
egl_buffer_reader: Option<&EGLBufferReader>,
(x, y): (i32, i32),
token: MyCompositorToken,
log: &Logger,
) -> Result<(), SwapBuffersError>
2021-04-28 22:32:47 +00:00
where
2021-05-13 21:33:22 +00:00
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
2021-05-23 21:03:43 +00:00
F: Frame<Error = E, TextureId = T>,
2021-04-28 22:32:47 +00:00
E: std::error::Error + Into<SwapBuffersError>,
T: Texture + 'static,
{
let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
Ok(h) => h,
Err(_) => {
warn!(
log,
"Trying to display as a cursor a surface that does not have the CursorImage role."
);
(0, 0)
}
};
2021-05-26 17:12:45 +00:00
draw_surface_tree(
renderer,
frame,
surface,
#[cfg(feature = "egl")]
2021-05-26 17:12:45 +00:00
egl_buffer_reader,
(x - dx, y - dy),
token,
log,
)
}
2021-05-23 21:03:43 +00:00
fn draw_surface_tree<R, E, F, T>(
renderer: &mut R,
2021-05-23 21:03:43 +00:00
frame: &mut F,
root: &wl_surface::WlSurface,
#[cfg(feature = "egl")]
2021-05-13 17:59:47 +00:00
egl_buffer_reader: Option<&EGLBufferReader>,
location: (i32, i32),
compositor_token: MyCompositorToken,
log: &Logger,
) -> Result<(), SwapBuffersError>
2021-04-28 22:32:47 +00:00
where
2021-05-13 21:33:22 +00:00
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
2021-05-23 21:03:43 +00:00
F: Frame<Error = E, TextureId = T>,
2021-04-28 22:32:47 +00:00
E: std::error::Error + Into<SwapBuffersError>,
T: Texture + 'static,
{
let mut result = Ok(());
compositor_token.with_surface_tree_upward(
root,
2021-05-23 21:03:43 +00:00
location,
|_surface, attributes, role, &(mut x, mut y)| {
// Pull a new buffer if available
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut();
if data.texture.is_none() {
if let Some(buffer) = data.current_state.buffer.take() {
let texture = match buffer_type(
&buffer,
#[cfg(feature = "egl")]
egl_buffer_reader
) {
Some(BufferType::Shm) => {
let damage = attributes
.damage
.iter()
.map(|dmg| match dmg {
Damage::Buffer(rect) => *rect,
// TODO also apply transformations
Damage::Surface(rect) => rect.scale(attributes.buffer_scale),
})
.collect::<Vec<_>>();
let result = renderer.import_shm_buffer(&buffer, Some(&attributes), &damage);
2021-06-09 19:02:33 +00:00
buffer.release();
// don't return the buffer as it is already released
Some((result, None))
},
#[cfg(feature = "egl")]
2021-06-09 19:02:33 +00:00
Some(BufferType::Egl) => Some((renderer.import_egl_buffer(&buffer, egl_buffer_reader.unwrap()), Some(buffer))),
Some(BufferType::Dma) => Some((renderer.import_dma_buffer(&buffer), Some(buffer))),
_ => {
error!(log, "Unknown buffer format for: {:?}", buffer);
2021-06-09 19:02:33 +00:00
buffer.release();
None
}
};
match texture {
2021-06-09 19:02:33 +00:00
Some((Ok(m), buffer)) => {
data.texture = Some(Box::new(BufferTextures { buffer, texture: m })
2021-05-15 16:17:43 +00:00
as Box<dyn std::any::Any + 'static>)
}
// there was an error reading the buffer, release it.
2021-06-09 19:02:33 +00:00
Some((Err(err), buffer)) => {
2021-04-26 19:44:40 +00:00
warn!(log, "Error loading buffer: {:?}", err);
2021-06-09 19:02:33 +00:00
if let Some(buffer) = buffer {
buffer.release();
}
},
2021-06-09 19:02:33 +00:00
None => {},
};
}
}
// Now, should we be drawn ?
2021-05-26 17:12:45 +00:00
if data.texture.is_some() {
// if yes, also process the children
if Role::<SubsurfaceRole>::has(role) {
x += data.current_state.sub_location.0;
y += data.current_state.sub_location.1;
}
TraversalAction::DoChildren((x, y))
} else {
// we are not displayed, so our children are neither
TraversalAction::SkipChildren
}
} else {
// we are not displayed, so our children are neither
TraversalAction::SkipChildren
}
},
|_surface, attributes, role, &(mut x, mut y)| {
if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut();
let (sub_x, sub_y) = data.current_state.sub_location;
2021-05-13 17:59:47 +00:00
if let Some(texture) = data
2021-04-28 22:32:47 +00:00
.texture
.as_mut()
.and_then(|x| x.downcast_mut::<BufferTextures<T>>())
{
// we need to re-extract the subsurface offset, as the previous closure
// only passes it to our children
if Role::<SubsurfaceRole>::has(role) {
x += sub_x;
y += sub_y;
}
2021-05-23 21:03:43 +00:00
if let Err(err) = frame.render_texture_at(
2021-05-15 16:17:43 +00:00
&texture.texture,
(x, y),
Transform::Normal, /* TODO */
1.0,
) {
result = Err(err.into());
}
}
}
},
|_, _, _, _| true,
);
result
}
2021-05-23 21:03:43 +00:00
pub fn draw_windows<R, E, F, T>(
renderer: &mut R,
2021-05-23 21:03:43 +00:00
frame: &mut F,
#[cfg(feature = "egl")]
2021-05-13 17:59:47 +00:00
egl_buffer_reader: Option<&EGLBufferReader>,
window_map: &MyWindowMap,
output_rect: Option<Rectangle>,
compositor_token: MyCompositorToken,
log: &::slog::Logger,
) -> Result<(), SwapBuffersError>
2021-04-28 22:32:47 +00:00
where
2021-05-13 21:33:22 +00:00
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
2021-05-23 21:03:43 +00:00
F: Frame<Error = E, TextureId = T>,
2021-04-28 22:32:47 +00:00
E: std::error::Error + Into<SwapBuffersError>,
T: Texture + 'static,
{
let mut result = Ok(());
// redraw the frame, in a simple but inneficient way
2021-04-28 22:32:47 +00:00
window_map.with_windows_from_bottom_to_top(|toplevel_surface, mut initial_place, bounding_box| {
// skip windows that do not overlap with a given output
if let Some(output) = output_rect {
if !output.overlaps(bounding_box) {
return;
}
2021-04-28 22:32:47 +00:00
initial_place.0 -= output.x;
}
if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn
if let Err(err) = draw_surface_tree(
renderer,
2021-05-23 21:03:43 +00:00
frame,
2021-04-28 22:32:47 +00:00
&wl_surface,
#[cfg(feature = "egl")]
2021-05-13 17:59:47 +00:00
egl_buffer_reader,
2021-04-28 22:32:47 +00:00
initial_place,
compositor_token,
log,
) {
result = Err(err);
}
2021-04-28 22:32:47 +00:00
}
});
result
}
2021-05-23 21:03:43 +00:00
pub fn draw_dnd_icon<R, E, F, T>(
renderer: &mut R,
2021-05-23 21:03:43 +00:00
frame: &mut F,
surface: &wl_surface::WlSurface,
#[cfg(feature = "egl")]
2021-05-13 17:59:47 +00:00
egl_buffer_reader: Option<&EGLBufferReader>,
(x, y): (i32, i32),
token: MyCompositorToken,
log: &::slog::Logger,
) -> Result<(), SwapBuffersError>
2021-04-28 22:32:47 +00:00
where
2021-05-13 21:33:22 +00:00
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
2021-05-23 21:03:43 +00:00
F: Frame<Error = E, TextureId = T>,
2021-04-28 22:32:47 +00:00
E: std::error::Error + Into<SwapBuffersError>,
T: Texture + 'static,
{
if !token.has_role::<DnDIconRole>(surface) {
warn!(
log,
"Trying to display as a dnd icon a surface that does not have the DndIcon role."
);
}
draw_surface_tree(
renderer,
frame,
surface,
#[cfg(feature = "egl")]
egl_buffer_reader,
(x, y),
token,
log
)
}