smithay/anvil/src/drawing.rs

263 lines
9.1 KiB
Rust
Raw Normal View History

2021-04-28 22:31:49 +00:00
#![allow(clippy::too_many_arguments)]
use std::{cell::RefCell, sync::Mutex};
use slog::Logger;
use smithay::{
2021-05-13 17:59:47 +00:00
backend::{
renderer::{buffer_type, BufferType, Frame, ImportAll, Renderer, Texture, Transform},
2021-05-13 17:59:47 +00:00
SwapBuffersError,
},
reexports::wayland_server::protocol::{wl_buffer, wl_surface},
utils::{Logical, Point, Rectangle},
wayland::{
compositor::{
get_role, with_states, with_surface_tree_upward, Damage, SubsurfaceCachedState,
SurfaceAttributes, TraversalAction,
},
seat::CursorImageAttributes,
},
};
use crate::{shell::SurfaceData, window_map::WindowMap};
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,
location: Point<i32, Logical>,
output_scale: f32,
log: &Logger,
) -> Result<(), SwapBuffersError>
2021-04-28 22:32:47 +00:00
where
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
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 ret = with_states(surface, |states| {
Some(
states
.data_map
.get::<Mutex<CursorImageAttributes>>()
.unwrap()
.lock()
.unwrap()
.hotspot,
)
})
.unwrap_or(None);
let delta = match ret {
Some(h) => h,
None => {
warn!(
log,
"Trying to display as a cursor a surface that does not have the CursorImage role."
);
(0, 0).into()
}
};
draw_surface_tree(renderer, frame, surface, location - delta, output_scale, 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,
location: Point<i32, Logical>,
output_scale: f32,
log: &Logger,
) -> Result<(), SwapBuffersError>
2021-04-28 22:32:47 +00:00
where
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
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(());
with_surface_tree_upward(
root,
2021-05-23 21:03:43 +00:00
location,
|_surface, states, location| {
let mut location = *location;
// Pull a new buffer if available
if let Some(data) = states.data_map.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut();
let attributes = states.cached_state.current::<SurfaceAttributes>();
if data.texture.is_none() {
if let Some(buffer) = data.buffer.take() {
let damage = attributes
.damage
.iter()
.map(|dmg| match dmg {
Damage::Buffer(rect) => *rect,
// TODO also apply transformations
Damage::Surface(rect) => rect.to_buffer(attributes.buffer_scale),
})
.collect::<Vec<_>>();
match renderer.import_buffer(&buffer, Some(states), &damage) {
Some(Ok(m)) => {
let texture_buffer = if let Some(BufferType::Shm) = buffer_type(&buffer) {
buffer.release();
None
} else {
Some(buffer)
};
data.texture = Some(Box::new(BufferTextures {
buffer: texture_buffer,
texture: m,
}))
2021-06-09 20:12:35 +00:00
}
Some(Err(err)) => {
warn!(log, "Error loading buffer: {:?}", err);
buffer.release();
2021-06-09 20:12:35 +00:00
}
None => {
error!(log, "Unknown buffer format for: {:?}", buffer);
2021-06-09 19:02:33 +00:00
buffer.release();
}
}
}
}
// 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 states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location;
}
TraversalAction::DoChildren(location)
} 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, states, location| {
let mut location = *location;
if let Some(ref data) = states.data_map.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut();
2021-07-10 21:50:01 +00:00
let buffer_scale = data.buffer_scale;
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 states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location;
}
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,
location.to_f64().to_physical(output_scale as f64).to_i32_round(),
2021-07-10 21:50:01 +00:00
buffer_scale,
output_scale as f64,
Transform::Normal, /* TODO */
2021-05-15 16:17:43 +00:00
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,
window_map: &WindowMap,
output_rect: Rectangle<i32, Logical>,
output_scale: f32,
log: &::slog::Logger,
) -> Result<(), SwapBuffersError>
2021-04-28 22:32:47 +00:00
where
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
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
window_map.with_windows_from_bottom_to_top(|toplevel_surface, mut initial_place, &bounding_box| {
2021-04-28 22:32:47 +00:00
// skip windows that do not overlap with a given output
if !output_rect.overlaps(bounding_box) {
return;
2021-04-28 22:32:47 +00:00
}
initial_place.x -= output_rect.loc.x;
2021-04-28 22:32:47 +00:00
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, frame, &wl_surface, initial_place, output_scale, log)
{
2021-04-28 22:32:47 +00:00
result = Err(err);
}
2021-06-29 15:30:48 +00:00
// furthermore, draw its popups
let toplevel_geometry_offset = window_map
.geometry(toplevel_surface)
.map(|g| g.loc)
.unwrap_or_default();
2021-06-29 15:30:48 +00:00
window_map.with_child_popups(&wl_surface, |popup| {
let location = popup.location();
let draw_location = initial_place + location + toplevel_geometry_offset;
2021-06-29 15:30:48 +00:00
if let Some(wl_surface) = popup.get_surface() {
if let Err(err) =
draw_surface_tree(renderer, frame, &wl_surface, draw_location, output_scale, log)
{
2021-06-29 15:30:48 +00:00
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,
location: Point<i32, Logical>,
output_scale: f32,
log: &::slog::Logger,
) -> Result<(), SwapBuffersError>
2021-04-28 22:32:47 +00:00
where
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
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 get_role(surface) != Some("dnd_icon") {
warn!(
log,
"Trying to display as a dnd icon a surface that does not have the DndIcon role."
);
}
draw_surface_tree(renderer, frame, surface, location, output_scale, log)
}