2021-04-28 22:31:49 +00:00
|
|
|
#![allow(clippy::too_many_arguments)]
|
|
|
|
|
2021-05-27 15:34:37 +00:00
|
|
|
use std::cell::RefCell;
|
2021-04-06 23:15:03 +00:00
|
|
|
|
|
|
|
use slog::Logger;
|
|
|
|
use smithay::{
|
2021-05-13 17:59:47 +00:00
|
|
|
backend::{
|
2021-06-05 17:58:51 +00:00
|
|
|
renderer::{Frame, Renderer, Texture, Transform, ImportShm, BufferType, buffer_type},
|
2021-05-13 17:59:47 +00:00
|
|
|
SwapBuffersError,
|
|
|
|
},
|
2021-05-27 15:34:37 +00:00
|
|
|
reexports::wayland_server::protocol::{wl_buffer, wl_surface},
|
2021-04-06 23:15:03 +00:00
|
|
|
utils::Rectangle,
|
|
|
|
wayland::{
|
2021-05-26 17:12:45 +00:00
|
|
|
compositor::{roles::Role, Damage, SubsurfaceRole, TraversalAction},
|
2021-04-06 23:15:03 +00:00
|
|
|
data_device::DnDIconRole,
|
|
|
|
seat::CursorImageRole,
|
|
|
|
},
|
|
|
|
};
|
2021-06-05 17:58:51 +00:00
|
|
|
#[cfg(feature = "egl")]
|
|
|
|
use smithay::backend::{
|
|
|
|
egl::display::EGLBufferReader,
|
|
|
|
renderer::ImportEgl
|
|
|
|
};
|
|
|
|
// hacky...
|
|
|
|
#[cfg(not(feature = "egl"))]
|
|
|
|
pub trait ImportEgl {}
|
|
|
|
#[cfg(not(feature = "egl"))]
|
|
|
|
impl<T> ImportEgl for T {}
|
2021-04-06 23:15:03 +00:00
|
|
|
|
|
|
|
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
|
2021-04-25 21:38:48 +00:00
|
|
|
|
2021-05-13 17:59:47 +00:00
|
|
|
struct BufferTextures<T> {
|
2021-05-16 18:08:10 +00:00
|
|
|
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) {
|
2021-05-16 18:08:10 +00:00
|
|
|
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>(
|
2021-04-06 23:15:03 +00:00
|
|
|
renderer: &mut R,
|
2021-05-23 21:03:43 +00:00
|
|
|
frame: &mut F,
|
2021-04-06 23:15:03 +00:00
|
|
|
surface: &wl_surface::WlSurface,
|
2021-06-05 17:58:51 +00:00
|
|
|
#[cfg(feature = "egl")]
|
2021-05-13 17:59:47 +00:00
|
|
|
egl_buffer_reader: Option<&EGLBufferReader>,
|
2021-04-06 23:15:03 +00:00
|
|
|
(x, y): (i32, i32),
|
|
|
|
token: MyCompositorToken,
|
|
|
|
log: &Logger,
|
2021-04-28 22:02:17 +00:00
|
|
|
) -> Result<(), SwapBuffersError>
|
2021-04-28 22:32:47 +00:00
|
|
|
where
|
2021-06-05 17:58:51 +00:00
|
|
|
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl,
|
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,
|
2021-04-06 23:15:03 +00:00
|
|
|
{
|
|
|
|
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,
|
2021-06-05 17:58:51 +00:00
|
|
|
#[cfg(feature = "egl")]
|
2021-05-26 17:12:45 +00:00
|
|
|
egl_buffer_reader,
|
|
|
|
(x - dx, y - dy),
|
|
|
|
token,
|
|
|
|
log,
|
|
|
|
)
|
2021-04-06 23:15:03 +00:00
|
|
|
}
|
|
|
|
|
2021-05-23 21:03:43 +00:00
|
|
|
fn draw_surface_tree<R, E, F, T>(
|
2021-04-06 23:15:03 +00:00
|
|
|
renderer: &mut R,
|
2021-05-23 21:03:43 +00:00
|
|
|
frame: &mut F,
|
2021-04-06 23:15:03 +00:00
|
|
|
root: &wl_surface::WlSurface,
|
2021-06-05 17:58:51 +00:00
|
|
|
#[cfg(feature = "egl")]
|
2021-05-13 17:59:47 +00:00
|
|
|
egl_buffer_reader: Option<&EGLBufferReader>,
|
2021-04-06 23:15:03 +00:00
|
|
|
location: (i32, i32),
|
|
|
|
compositor_token: MyCompositorToken,
|
|
|
|
log: &Logger,
|
2021-04-28 22:02:17 +00:00
|
|
|
) -> Result<(), SwapBuffersError>
|
2021-04-28 22:32:47 +00:00
|
|
|
where
|
2021-06-05 17:58:51 +00:00
|
|
|
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl,
|
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,
|
2021-04-06 23:15:03 +00:00
|
|
|
{
|
2021-04-28 22:02:17 +00:00
|
|
|
let mut result = Ok(());
|
|
|
|
|
2021-04-06 23:15:03 +00:00
|
|
|
compositor_token.with_surface_tree_upward(
|
|
|
|
root,
|
2021-05-23 21:03:43 +00:00
|
|
|
location,
|
|
|
|
|_surface, attributes, role, &(mut x, mut y)| {
|
2021-04-06 23:15:03 +00:00
|
|
|
// 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() {
|
2021-06-05 17:58:51 +00:00
|
|
|
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);
|
|
|
|
if result.is_ok() {
|
2021-05-16 18:08:10 +00:00
|
|
|
buffer.release();
|
2021-06-05 17:58:51 +00:00
|
|
|
}
|
|
|
|
Some(result)
|
|
|
|
},
|
|
|
|
#[cfg(feature = "egl")]
|
|
|
|
Some(BufferType::Egl) => Some(renderer.import_egl_buffer(&buffer, egl_buffer_reader.unwrap())),
|
|
|
|
_ => {
|
|
|
|
error!(log, "Unknown buffer format for: {:?}", buffer);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
match texture {
|
|
|
|
Some(Ok(m)) => {
|
|
|
|
data.texture = Some(Box::new(BufferTextures { buffer: Some(buffer), texture: m })
|
2021-05-15 16:17:43 +00:00
|
|
|
as Box<dyn std::any::Any + 'static>)
|
|
|
|
}
|
2021-06-05 17:58:51 +00:00
|
|
|
// there was an error reading the buffer, release it.
|
|
|
|
Some(Err(err)) => {
|
2021-04-26 19:44:40 +00:00
|
|
|
warn!(log, "Error loading buffer: {:?}", err);
|
2021-05-13 17:59:47 +00:00
|
|
|
buffer.release();
|
2021-06-05 17:58:51 +00:00
|
|
|
},
|
|
|
|
None => buffer.release(),
|
2021-04-06 23:15:03 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now, should we be drawn ?
|
2021-05-26 17:12:45 +00:00
|
|
|
if data.texture.is_some() {
|
|
|
|
// if yes, also process the children
|
2021-04-06 23:15:03 +00:00
|
|
|
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>>())
|
|
|
|
{
|
2021-04-06 23:15:03 +00:00
|
|
|
// 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,
|
|
|
|
) {
|
2021-04-28 22:02:17 +00:00
|
|
|
result = Err(err.into());
|
|
|
|
}
|
2021-04-06 23:15:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|_, _, _, _| true,
|
|
|
|
);
|
2021-04-28 22:02:17 +00:00
|
|
|
|
|
|
|
result
|
2021-04-06 23:15:03 +00:00
|
|
|
}
|
|
|
|
|
2021-05-23 21:03:43 +00:00
|
|
|
pub fn draw_windows<R, E, F, T>(
|
2021-04-06 23:15:03 +00:00
|
|
|
renderer: &mut R,
|
2021-05-23 21:03:43 +00:00
|
|
|
frame: &mut F,
|
2021-06-05 17:58:51 +00:00
|
|
|
#[cfg(feature = "egl")]
|
2021-05-13 17:59:47 +00:00
|
|
|
egl_buffer_reader: Option<&EGLBufferReader>,
|
2021-04-06 23:15:03 +00:00
|
|
|
window_map: &MyWindowMap,
|
|
|
|
output_rect: Option<Rectangle>,
|
|
|
|
compositor_token: MyCompositorToken,
|
|
|
|
log: &::slog::Logger,
|
2021-04-28 22:02:17 +00:00
|
|
|
) -> Result<(), SwapBuffersError>
|
2021-04-28 22:32:47 +00:00
|
|
|
where
|
2021-06-05 17:58:51 +00:00
|
|
|
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl,
|
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,
|
2021-04-06 23:15:03 +00:00
|
|
|
{
|
2021-04-28 22:02:17 +00:00
|
|
|
let mut result = Ok(());
|
|
|
|
|
2021-04-06 23:15:03 +00:00
|
|
|
// 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:02:17 +00:00
|
|
|
}
|
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,
|
2021-06-05 17:58:51 +00:00
|
|
|
#[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:02:17 +00:00
|
|
|
}
|
2021-04-28 22:32:47 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-04-28 22:02:17 +00:00
|
|
|
result
|
2021-04-06 23:15:03 +00:00
|
|
|
}
|
|
|
|
|
2021-05-23 21:03:43 +00:00
|
|
|
pub fn draw_dnd_icon<R, E, F, T>(
|
2021-04-06 23:15:03 +00:00
|
|
|
renderer: &mut R,
|
2021-05-23 21:03:43 +00:00
|
|
|
frame: &mut F,
|
2021-04-06 23:15:03 +00:00
|
|
|
surface: &wl_surface::WlSurface,
|
2021-06-05 17:58:51 +00:00
|
|
|
#[cfg(feature = "egl")]
|
2021-05-13 17:59:47 +00:00
|
|
|
egl_buffer_reader: Option<&EGLBufferReader>,
|
2021-04-06 23:15:03 +00:00
|
|
|
(x, y): (i32, i32),
|
|
|
|
token: MyCompositorToken,
|
|
|
|
log: &::slog::Logger,
|
2021-04-28 22:02:17 +00:00
|
|
|
) -> Result<(), SwapBuffersError>
|
2021-04-28 22:32:47 +00:00
|
|
|
where
|
2021-06-05 17:58:51 +00:00
|
|
|
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl,
|
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,
|
2021-04-06 23:15:03 +00:00
|
|
|
{
|
|
|
|
if !token.has_role::<DnDIconRole>(surface) {
|
|
|
|
warn!(
|
|
|
|
log,
|
|
|
|
"Trying to display as a dnd icon a surface that does not have the DndIcon role."
|
|
|
|
);
|
|
|
|
}
|
2021-06-05 17:58:51 +00:00
|
|
|
draw_surface_tree(
|
|
|
|
renderer,
|
|
|
|
frame,
|
|
|
|
surface,
|
|
|
|
#[cfg(feature = "egl")]
|
|
|
|
egl_buffer_reader,
|
|
|
|
(x, y),
|
|
|
|
token,
|
|
|
|
log
|
|
|
|
)
|
2021-04-06 23:15:03 +00:00
|
|
|
}
|