Merge pull request #221 from Smithay/anvil/outputs
anvil: support multiple outputs
This commit is contained in:
commit
b75d6524ce
|
@ -1,13 +1,25 @@
|
||||||
|
use glium::texture::Texture2d;
|
||||||
|
#[cfg(feature = "egl")]
|
||||||
|
use glium::{
|
||||||
|
texture::{MipmapsOption, UncompressedFloatFormat},
|
||||||
|
GlObject,
|
||||||
|
};
|
||||||
|
use slog::Logger;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
#[cfg(feature = "egl")]
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use slog::Logger;
|
|
||||||
|
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
use smithay::backend::egl::display::EGLBufferReader;
|
use smithay::backend::egl::{
|
||||||
use smithay::{
|
display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLImages, Format,
|
||||||
reexports::wayland_server::protocol::wl_buffer::WlBuffer,
|
|
||||||
wayland::shm::with_buffer_contents as shm_buffer_contents,
|
|
||||||
};
|
};
|
||||||
|
use smithay::{
|
||||||
|
backend::graphics::gl::GLGraphicsBackend,
|
||||||
|
reexports::wayland_server::protocol::wl_buffer::WlBuffer,
|
||||||
|
wayland::shm::{with_buffer_contents as shm_buffer_contents, BufferAccessError},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::glium_drawer::GliumDrawer;
|
||||||
|
|
||||||
/// Utilities for working with `WlBuffer`s.
|
/// Utilities for working with `WlBuffer`s.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -41,22 +53,182 @@ impl BufferUtils {
|
||||||
.borrow()
|
.borrow()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|display| display.egl_buffer_dimensions(buffer))
|
.and_then(|display| display.egl_buffer_dimensions(buffer))
|
||||||
.or_else(|| self.shm_buffer_dimensions(buffer))
|
.or_else(|| self.shm_buffer_dimensions(buffer).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the dimensions of an image stored in the buffer.
|
/// Returns the dimensions of an image stored in the buffer.
|
||||||
#[cfg(not(feature = "egl"))]
|
#[cfg(not(feature = "egl"))]
|
||||||
pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
|
pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
|
||||||
self.shm_buffer_dimensions(buffer)
|
self.shm_buffer_dimensions(buffer).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the dimensions of an image stored in the shm buffer.
|
/// Returns the dimensions of an image stored in the shm buffer.
|
||||||
fn shm_buffer_dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
|
fn shm_buffer_dimensions(&self, buffer: &WlBuffer) -> Result<(i32, i32), BufferAccessError> {
|
||||||
shm_buffer_contents(buffer, |_, data| (data.width, data.height))
|
shm_buffer_contents(buffer, |_, data| (data.width, data.height)).map_err(|err| {
|
||||||
.map_err(|err| {
|
|
||||||
warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err));
|
warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err));
|
||||||
err
|
err
|
||||||
})
|
})
|
||||||
.ok()
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egl")]
|
||||||
|
pub fn load_buffer(&self, buffer: WlBuffer) -> Result<BufferTextures, WlBuffer> {
|
||||||
|
// try to retrieve the egl contents of this buffer
|
||||||
|
let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() {
|
||||||
|
display.egl_buffer_contents(&buffer)
|
||||||
|
} else {
|
||||||
|
return Err(buffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
match images {
|
||||||
|
Ok(images) => {
|
||||||
|
// we have an EGL buffer
|
||||||
|
Ok(BufferTextures {
|
||||||
|
buffer,
|
||||||
|
textures: HashMap::new(),
|
||||||
|
fragment: crate::shaders::BUFFER_RGBA,
|
||||||
|
y_inverted: images.y_inverted,
|
||||||
|
dimensions: (images.width, images.height),
|
||||||
|
images: Some(images), // I guess we need to keep this alive ?
|
||||||
|
logger: self.log.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(EGLBufferAccessError::NotManaged(_)) => {
|
||||||
|
// this is not an EGL buffer, try SHM
|
||||||
|
self.load_shm_buffer(buffer)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(self.log, "EGL error"; "err" => format!("{:?}", err));
|
||||||
|
Err(buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "egl"))]
|
||||||
|
pub fn load_buffer(&self, buffer: WlBuffer) -> Result<BufferTextures, WlBuffer> {
|
||||||
|
self.load_shm_buffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_shm_buffer(&self, buffer: WlBuffer) -> Result<BufferTextures, WlBuffer> {
|
||||||
|
let (width, height, format) =
|
||||||
|
match shm_buffer_contents(&buffer, |_, data| (data.width, data.height, data.format)) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err));
|
||||||
|
return Err(buffer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let shader = match crate::shm_load::load_format(format) {
|
||||||
|
Ok(x) => x.1,
|
||||||
|
Err(format) => {
|
||||||
|
warn!(self.log, "Unable to load buffer format: {:?}", format);
|
||||||
|
return Err(buffer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(BufferTextures {
|
||||||
|
buffer,
|
||||||
|
textures: HashMap::new(),
|
||||||
|
fragment: shader,
|
||||||
|
y_inverted: false,
|
||||||
|
dimensions: (width as u32, height as u32),
|
||||||
|
#[cfg(feature = "egl")]
|
||||||
|
images: None,
|
||||||
|
logger: self.log.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BufferTextures {
|
||||||
|
buffer: WlBuffer,
|
||||||
|
pub textures: HashMap<usize, Texture2d>,
|
||||||
|
pub fragment: usize,
|
||||||
|
pub y_inverted: bool,
|
||||||
|
pub dimensions: (u32, u32),
|
||||||
|
#[cfg(feature = "egl")]
|
||||||
|
images: Option<EGLImages>,
|
||||||
|
logger: slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferTextures {
|
||||||
|
#[cfg(feature = "egl")]
|
||||||
|
pub fn load_texture<'a, F: GLGraphicsBackend + 'static>(
|
||||||
|
&'a mut self,
|
||||||
|
drawer: &GliumDrawer<F>,
|
||||||
|
) -> Result<&'a Texture2d, ()> {
|
||||||
|
if self.textures.contains_key(&drawer.id) {
|
||||||
|
return Ok(&self.textures[&drawer.id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(images) = self.images.as_ref() {
|
||||||
|
//EGL buffer
|
||||||
|
let format = match images.format {
|
||||||
|
Format::RGB => UncompressedFloatFormat::U8U8U8,
|
||||||
|
Format::RGBA => UncompressedFloatFormat::U8U8U8U8,
|
||||||
|
_ => {
|
||||||
|
warn!(self.logger, "Unsupported EGL buffer format"; "format" => format!("{:?}", images.format));
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let opengl_texture = Texture2d::empty_with_format(
|
||||||
|
&drawer.display,
|
||||||
|
format,
|
||||||
|
MipmapsOption::NoMipmap,
|
||||||
|
images.width,
|
||||||
|
images.height,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
images
|
||||||
|
.bind_to_texture(0, opengl_texture.get_id(), &*drawer.display.borrow())
|
||||||
|
.expect("Failed to bind to texture");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.textures.insert(drawer.id, opengl_texture);
|
||||||
|
Ok(&self.textures[&drawer.id])
|
||||||
|
} else {
|
||||||
|
self.load_shm_texture(drawer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "egl"))]
|
||||||
|
pub fn load_texture<'a, F: GLGraphicsBackend + 'static>(
|
||||||
|
&'a mut self,
|
||||||
|
drawer: &GliumDrawer<F>,
|
||||||
|
) -> Result<&'a Texture2d, ()> {
|
||||||
|
if self.textures.contains_key(&drawer.id) {
|
||||||
|
return Ok(&self.textures[&drawer.id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.load_shm_texture(drawer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_shm_texture<'a, F: GLGraphicsBackend + 'static>(
|
||||||
|
&'a mut self,
|
||||||
|
drawer: &GliumDrawer<F>,
|
||||||
|
) -> Result<&'a Texture2d, ()> {
|
||||||
|
match shm_buffer_contents(&self.buffer, |slice, data| {
|
||||||
|
crate::shm_load::load_shm_buffer(data, slice)
|
||||||
|
.map(|(image, _kind)| Texture2d::new(&drawer.display, image).unwrap())
|
||||||
|
}) {
|
||||||
|
Ok(Ok(texture)) => {
|
||||||
|
self.textures.insert(drawer.id, texture);
|
||||||
|
Ok(&self.textures[&drawer.id])
|
||||||
|
}
|
||||||
|
Ok(Err(format)) => {
|
||||||
|
warn!(self.logger, "Unsupported SHM buffer format"; "format" => format!("{:?}", format));
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(self.logger, "Unable to load buffer contents"; "err" => format!("{:?}", err));
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for BufferTextures {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.buffer.release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,33 @@
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefCell},
|
cell::{Ref, RefCell},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use glium::{
|
use glium::{self, index::PrimitiveType, texture::Texture2d, Surface};
|
||||||
self,
|
|
||||||
index::PrimitiveType,
|
|
||||||
texture::{MipmapsOption, Texture2d, UncompressedFloatFormat},
|
|
||||||
GlObject, Surface,
|
|
||||||
};
|
|
||||||
use slog::Logger;
|
use slog::Logger;
|
||||||
|
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
use smithay::backend::egl::display::EGLBufferReader;
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::graphics::{
|
||||||
egl::{BufferAccessError, EGLImages, Format},
|
|
||||||
graphics::{
|
|
||||||
gl::GLGraphicsBackend,
|
gl::GLGraphicsBackend,
|
||||||
glium::{Frame, GliumGraphicsBackend},
|
glium::{Frame, GliumGraphicsBackend},
|
||||||
SwapBuffersError,
|
CursorBackend, SwapBuffersError,
|
||||||
},
|
|
||||||
},
|
|
||||||
reexports::{
|
|
||||||
calloop::LoopHandle,
|
|
||||||
wayland_server::protocol::{wl_buffer, wl_surface},
|
|
||||||
},
|
},
|
||||||
|
reexports::{calloop::LoopHandle, wayland_server::protocol::wl_surface},
|
||||||
|
utils::Rectangle,
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::{roles::Role, SubsurfaceRole, TraversalAction},
|
compositor::{roles::Role, SubsurfaceRole, TraversalAction},
|
||||||
data_device::DnDIconRole,
|
data_device::DnDIconRole,
|
||||||
seat::CursorImageRole,
|
seat::CursorImageRole,
|
||||||
shm::with_buffer_contents as shm_buffer_contents,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::buffer_utils::BufferUtils;
|
||||||
use crate::shaders;
|
use crate::shaders;
|
||||||
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
|
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
|
||||||
|
|
||||||
|
pub static BACKEND_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
position: [f32; 2],
|
position: [f32; 2],
|
||||||
|
@ -51,12 +42,13 @@ mod implement_vertex {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GliumDrawer<F: GLGraphicsBackend + 'static> {
|
pub struct GliumDrawer<F: GLGraphicsBackend + 'static> {
|
||||||
display: GliumGraphicsBackend<F>,
|
pub id: usize,
|
||||||
|
pub display: GliumGraphicsBackend<F>,
|
||||||
vertex_buffer: glium::VertexBuffer<Vertex>,
|
vertex_buffer: glium::VertexBuffer<Vertex>,
|
||||||
index_buffer: glium::IndexBuffer<u16>,
|
index_buffer: glium::IndexBuffer<u16>,
|
||||||
programs: [glium::Program; shaders::FRAGMENT_COUNT],
|
programs: [glium::Program; shaders::FRAGMENT_COUNT],
|
||||||
#[cfg(feature = "egl")]
|
buffer_loader: BufferUtils,
|
||||||
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
pub hardware_cursor: AtomicBool,
|
||||||
log: Logger,
|
log: Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,56 +59,7 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Into<GliumGraphicsBackend<T>> + GLGraphicsBackend + 'static> GliumDrawer<T> {
|
impl<T: Into<GliumGraphicsBackend<T>> + GLGraphicsBackend + 'static> GliumDrawer<T> {
|
||||||
#[cfg(feature = "egl")]
|
pub fn init(backend: T, buffer_loader: BufferUtils, log: Logger) -> GliumDrawer<T> {
|
||||||
pub fn init(
|
|
||||||
backend: T,
|
|
||||||
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
|
||||||
log: Logger,
|
|
||||||
) -> GliumDrawer<T> {
|
|
||||||
let display = backend.into();
|
|
||||||
|
|
||||||
// building the vertex buffer, which contains all the vertices that we will draw
|
|
||||||
let vertex_buffer = glium::VertexBuffer::new(
|
|
||||||
&display,
|
|
||||||
&[
|
|
||||||
Vertex {
|
|
||||||
position: [0.0, 0.0],
|
|
||||||
tex_coords: [0.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.0, 1.0],
|
|
||||||
tex_coords: [0.0, 1.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [1.0, 1.0],
|
|
||||||
tex_coords: [1.0, 1.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [1.0, 0.0],
|
|
||||||
tex_coords: [1.0, 0.0],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// building the index buffer
|
|
||||||
let index_buffer =
|
|
||||||
glium::IndexBuffer::new(&display, PrimitiveType::TriangleStrip, &[1 as u16, 2, 0, 3]).unwrap();
|
|
||||||
|
|
||||||
let programs = opengl_programs!(&display);
|
|
||||||
|
|
||||||
GliumDrawer {
|
|
||||||
display,
|
|
||||||
vertex_buffer,
|
|
||||||
index_buffer,
|
|
||||||
programs,
|
|
||||||
egl_buffer_reader,
|
|
||||||
log,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "egl"))]
|
|
||||||
pub fn init(backend: T, log: Logger) -> GliumDrawer<T> {
|
|
||||||
let display = backend.into();
|
let display = backend.into();
|
||||||
|
|
||||||
// building the vertex buffer, which contains all the vertices that we will draw
|
// building the vertex buffer, which contains all the vertices that we will draw
|
||||||
|
@ -150,99 +93,98 @@ impl<T: Into<GliumGraphicsBackend<T>> + GLGraphicsBackend + 'static> GliumDrawer
|
||||||
let programs = opengl_programs!(&display);
|
let programs = opengl_programs!(&display);
|
||||||
|
|
||||||
GliumDrawer {
|
GliumDrawer {
|
||||||
|
id: BACKEND_COUNTER.fetch_add(1, Ordering::AcqRel),
|
||||||
display,
|
display,
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
index_buffer,
|
index_buffer,
|
||||||
programs,
|
programs,
|
||||||
|
buffer_loader,
|
||||||
|
hardware_cursor: AtomicBool::new(false),
|
||||||
log,
|
log,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F: GLGraphicsBackend + CursorBackend + 'static> GliumDrawer<F> {
|
||||||
|
pub fn draw_hardware_cursor(
|
||||||
|
&self,
|
||||||
|
cursor: &<F as CursorBackend>::CursorFormat,
|
||||||
|
hotspot: (u32, u32),
|
||||||
|
position: (i32, i32),
|
||||||
|
) {
|
||||||
|
let (x, y) = position;
|
||||||
|
let _ = self.display.borrow().set_cursor_position(x as u32, y as u32);
|
||||||
|
if !self.hardware_cursor.swap(true, Ordering::SeqCst)
|
||||||
|
&& self
|
||||||
|
.display
|
||||||
|
.borrow()
|
||||||
|
.set_cursor_representation(cursor, hotspot)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
warn!(self.log, "Failed to upload hardware cursor",);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_software_cursor(
|
||||||
|
&self,
|
||||||
|
frame: &mut Frame,
|
||||||
|
surface: &wl_surface::WlSurface,
|
||||||
|
(x, y): (i32, i32),
|
||||||
|
token: MyCompositorToken,
|
||||||
|
) {
|
||||||
|
let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
|
||||||
|
Ok(h) => h,
|
||||||
|
Err(_) => {
|
||||||
|
warn!(
|
||||||
|
self.log,
|
||||||
|
"Trying to display as a cursor a surface that does not have the CursorImage role."
|
||||||
|
);
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
|
||||||
|
self.draw_surface_tree(frame, surface, (x - dx, y - dy), token, screen_dimensions);
|
||||||
|
self.clear_cursor()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_cursor(&self) {
|
||||||
|
if self.hardware_cursor.swap(false, Ordering::SeqCst)
|
||||||
|
&& self.display.borrow().clear_cursor_representation().is_err()
|
||||||
|
{
|
||||||
|
warn!(self.log, "Failed to clear cursor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// I would love to do this (check on !CursorBackend), but this is essentially specialization...
|
||||||
|
// And since this is just an example compositor, it seems we require now,
|
||||||
|
// that for the use of software cursors we need the hardware cursor trait (to do automatic cleanup..)
|
||||||
|
/*
|
||||||
|
impl<F: GLGraphicsBackend + !CursorBackend + 'static> GliumDrawer<F> {
|
||||||
|
pub fn draw_software_cursor(
|
||||||
|
&self,
|
||||||
|
frame: &mut Frame,
|
||||||
|
surface: &wl_surface::WlSurface,
|
||||||
|
(x, y): (i32, i32),
|
||||||
|
token: MyCompositorToken,
|
||||||
|
) {
|
||||||
|
let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
|
||||||
|
Ok(h) => h,
|
||||||
|
Err(_) => {
|
||||||
|
warn!(
|
||||||
|
self.log,
|
||||||
|
"Trying to display as a cursor a surface that does not have the CursorImage role."
|
||||||
|
);
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
|
||||||
|
self.draw_surface_tree(frame, surface, (x - dx, y - dy), token, screen_dimensions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
pub fn texture_from_buffer(&self, buffer: wl_buffer::WlBuffer) -> Result<TextureMetadata, ()> {
|
|
||||||
// try to retrieve the egl contents of this buffer
|
|
||||||
let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() {
|
|
||||||
display.egl_buffer_contents(buffer)
|
|
||||||
} else {
|
|
||||||
Err(BufferAccessError::NotManaged(
|
|
||||||
buffer,
|
|
||||||
smithay::backend::egl::EGLError::BadDisplay,
|
|
||||||
))
|
|
||||||
};
|
|
||||||
match images {
|
|
||||||
Ok(images) => {
|
|
||||||
// we have an EGL buffer
|
|
||||||
let format = match images.format {
|
|
||||||
Format::RGB => UncompressedFloatFormat::U8U8U8,
|
|
||||||
Format::RGBA => UncompressedFloatFormat::U8U8U8U8,
|
|
||||||
_ => {
|
|
||||||
warn!(self.log, "Unsupported EGL buffer format"; "format" => format!("{:?}", images.format));
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let opengl_texture = Texture2d::empty_with_format(
|
|
||||||
&self.display,
|
|
||||||
format,
|
|
||||||
MipmapsOption::NoMipmap,
|
|
||||||
images.width,
|
|
||||||
images.height,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
unsafe {
|
|
||||||
images
|
|
||||||
.bind_to_texture(0, opengl_texture.get_id(), &*self.display.borrow())
|
|
||||||
.expect("Failed to bind to texture");
|
|
||||||
}
|
|
||||||
Ok(TextureMetadata {
|
|
||||||
texture: opengl_texture,
|
|
||||||
fragment: crate::shaders::BUFFER_RGBA,
|
|
||||||
y_inverted: images.y_inverted,
|
|
||||||
dimensions: (images.width, images.height),
|
|
||||||
images: Some(images), // I guess we need to keep this alive ?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Err(BufferAccessError::NotManaged(buffer, _)) => {
|
|
||||||
// this is not an EGL buffer, try SHM
|
|
||||||
self.texture_from_shm_buffer(buffer)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!(self.log, "EGL error"; "err" => format!("{:?}", err));
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "egl"))]
|
|
||||||
pub fn texture_from_buffer(&self, buffer: wl_buffer::WlBuffer) -> Result<TextureMetadata, ()> {
|
|
||||||
self.texture_from_shm_buffer(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn texture_from_shm_buffer(&self, buffer: wl_buffer::WlBuffer) -> Result<TextureMetadata, ()> {
|
|
||||||
match shm_buffer_contents(&buffer, |slice, data| {
|
|
||||||
crate::shm_load::load_shm_buffer(data, slice)
|
|
||||||
.map(|(image, kind)| (Texture2d::new(&self.display, image).unwrap(), kind, data))
|
|
||||||
}) {
|
|
||||||
Ok(Ok((texture, kind, data))) => Ok(TextureMetadata {
|
|
||||||
texture,
|
|
||||||
fragment: kind,
|
|
||||||
y_inverted: false,
|
|
||||||
dimensions: (data.width as u32, data.height as u32),
|
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
images: None,
|
|
||||||
}),
|
|
||||||
Ok(Err(format)) => {
|
|
||||||
warn!(self.log, "Unsupported SHM buffer format"; "format" => format!("{:?}", format));
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err));
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_texture(&self, target: &mut Frame, spec: RenderTextureSpec<'_>) {
|
pub fn render_texture(&self, target: &mut Frame, spec: RenderTextureSpec<'_>) {
|
||||||
let xscale = 2.0 * (spec.surface_dimensions.0 as f32) / (spec.screen_size.0 as f32);
|
let xscale = 2.0 * (spec.surface_dimensions.0 as f32) / (spec.screen_size.0 as f32);
|
||||||
let mut yscale = -2.0 * (spec.surface_dimensions.1 as f32) / (spec.screen_size.1 as f32);
|
let mut yscale = -2.0 * (spec.surface_dimensions.1 as f32) / (spec.screen_size.1 as f32);
|
||||||
|
@ -295,15 +237,6 @@ pub struct RenderTextureSpec<'a> {
|
||||||
blending: glium::Blend,
|
blending: glium::Blend,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TextureMetadata {
|
|
||||||
pub texture: Texture2d,
|
|
||||||
pub fragment: usize,
|
|
||||||
pub y_inverted: bool,
|
|
||||||
pub dimensions: (u32, u32),
|
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
images: Option<EGLImages>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
fn draw_surface_tree(
|
fn draw_surface_tree(
|
||||||
&self,
|
&self,
|
||||||
|
@ -322,25 +255,12 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
let mut data = data.borrow_mut();
|
let mut data = data.borrow_mut();
|
||||||
if data.texture.is_none() {
|
if data.texture.is_none() {
|
||||||
if let Some(buffer) = data.current_state.buffer.take() {
|
if let Some(buffer) = data.current_state.buffer.take() {
|
||||||
if let Ok(m) = self.texture_from_buffer(buffer.clone()) {
|
match self.buffer_loader.load_buffer(buffer) {
|
||||||
// release the buffer if it was an SHM buffer
|
Ok(m) => data.texture = Some(m),
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
{
|
|
||||||
if m.images.is_none() {
|
|
||||||
buffer.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "egl"))]
|
|
||||||
{
|
|
||||||
buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
data.texture = Some(m);
|
|
||||||
} else {
|
|
||||||
// there was an error reading the buffer, release it, we
|
// there was an error reading the buffer, release it, we
|
||||||
// already logged the error
|
// already logged the error
|
||||||
buffer.release();
|
Err(buffer) => buffer.release(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Now, should we be drawn ?
|
// Now, should we be drawn ?
|
||||||
|
@ -362,21 +282,26 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
},
|
},
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
||||||
let data = data.borrow();
|
let mut data = data.borrow_mut();
|
||||||
if let Some(ref metadata) = data.texture {
|
let (sub_x, sub_y) = data.current_state.sub_location;
|
||||||
|
if let Some(buffer_textures) = data.texture.as_mut() {
|
||||||
|
let texture_kind = buffer_textures.fragment;
|
||||||
|
let y_inverted = buffer_textures.y_inverted;
|
||||||
|
let surface_dimensions = buffer_textures.dimensions;
|
||||||
|
if let Ok(ref texture) = buffer_textures.load_texture(&self) {
|
||||||
// we need to re-extract the subsurface offset, as the previous closure
|
// we need to re-extract the subsurface offset, as the previous closure
|
||||||
// only passes it to our children
|
// only passes it to our children
|
||||||
if Role::<SubsurfaceRole>::has(role) {
|
if Role::<SubsurfaceRole>::has(role) {
|
||||||
x += data.current_state.sub_location.0;
|
x += sub_x;
|
||||||
y += data.current_state.sub_location.1;
|
y += sub_y;
|
||||||
}
|
}
|
||||||
self.render_texture(
|
self.render_texture(
|
||||||
frame,
|
frame,
|
||||||
RenderTextureSpec {
|
RenderTextureSpec {
|
||||||
texture: &metadata.texture,
|
texture: &texture,
|
||||||
texture_kind: metadata.fragment,
|
texture_kind,
|
||||||
y_inverted: metadata.y_inverted,
|
y_inverted,
|
||||||
surface_dimensions: metadata.dimensions,
|
surface_dimensions,
|
||||||
surface_location: (x, y),
|
surface_location: (x, y),
|
||||||
screen_size: screen_dimensions,
|
screen_size: screen_dimensions,
|
||||||
blending: ::glium::Blend {
|
blending: ::glium::Blend {
|
||||||
|
@ -394,6 +319,7 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|_, _, _, _| true,
|
|_, _, _, _| true,
|
||||||
);
|
);
|
||||||
|
@ -403,12 +329,21 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
&self,
|
&self,
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
window_map: &MyWindowMap,
|
window_map: &MyWindowMap,
|
||||||
|
output_rect: Option<Rectangle>,
|
||||||
compositor_token: MyCompositorToken,
|
compositor_token: MyCompositorToken,
|
||||||
) {
|
) {
|
||||||
// redraw the frame, in a simple but inneficient way
|
// redraw the frame, in a simple but inneficient way
|
||||||
{
|
{
|
||||||
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
|
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
|
||||||
window_map.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
|
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;
|
||||||
|
}
|
||||||
|
initial_place.0 -= output.x;
|
||||||
|
}
|
||||||
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||||
// this surface is a root of a subsurface tree that needs to be drawn
|
// this surface is a root of a subsurface tree that needs to be drawn
|
||||||
self.draw_surface_tree(
|
self.draw_surface_tree(
|
||||||
|
@ -419,29 +354,9 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
screen_dimensions,
|
screen_dimensions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_cursor(
|
|
||||||
&self,
|
|
||||||
frame: &mut Frame,
|
|
||||||
surface: &wl_surface::WlSurface,
|
|
||||||
(x, y): (i32, i32),
|
|
||||||
token: MyCompositorToken,
|
|
||||||
) {
|
|
||||||
let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
|
|
||||||
Ok(h) => h,
|
|
||||||
Err(_) => {
|
|
||||||
warn!(
|
|
||||||
self.log,
|
|
||||||
"Trying to display as a cursor a surface that does not have the CursorImage role."
|
|
||||||
);
|
);
|
||||||
(0, 0)
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
|
|
||||||
self.draw_surface_tree(frame, surface, (x - dx, y - dy), token, screen_dimensions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_dnd_icon(
|
pub fn draw_dnd_icon(
|
||||||
|
|
|
@ -82,6 +82,20 @@ impl AnvilState {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "udev")]
|
||||||
|
KeyAction::Screen(num) => {
|
||||||
|
let output_map = self.output_map.as_ref().unwrap();
|
||||||
|
let outputs = output_map.borrow();
|
||||||
|
if let Some(output) = outputs.get(num) {
|
||||||
|
let x = outputs
|
||||||
|
.iter()
|
||||||
|
.take(num)
|
||||||
|
.fold(0, |acc, output| acc + output.size.0) as f64
|
||||||
|
+ (output.size.0 as f64 / 2.0);
|
||||||
|
let y = output.size.1 as f64 / 2.0;
|
||||||
|
*self.pointer_location.borrow_mut() = (x as f64, y as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,10 +106,14 @@ impl AnvilState {
|
||||||
let mut location = self.pointer_location.borrow_mut();
|
let mut location = self.pointer_location.borrow_mut();
|
||||||
location.0 += x as f64;
|
location.0 += x as f64;
|
||||||
location.1 += y as f64;
|
location.1 += y as f64;
|
||||||
|
|
||||||
|
#[cfg(feature = "udev")]
|
||||||
|
{
|
||||||
// clamp to screen limits
|
// clamp to screen limits
|
||||||
// this event is never generated by winit so self.screen_size is relevant
|
// this event is never generated by winit
|
||||||
location.0 = (location.0).max(0.0).min(self.screen_size.0 as f64);
|
*location = self.clamp_coords(*location);
|
||||||
location.1 = (location.1).max(0.0).min(self.screen_size.1 as f64);
|
}
|
||||||
|
|
||||||
let under = self
|
let under = self
|
||||||
.window_map
|
.window_map
|
||||||
.borrow()
|
.borrow()
|
||||||
|
@ -110,8 +128,11 @@ impl AnvilState {
|
||||||
{
|
{
|
||||||
if self.session.is_some() {
|
if self.session.is_some() {
|
||||||
// we are started on a tty
|
// we are started on a tty
|
||||||
let (ux, uy) = evt.position_transformed(self.screen_size);
|
let x = self.pointer_location.borrow().0;
|
||||||
(ux as f64, uy as f64)
|
let screen_size = self.current_output_size(x);
|
||||||
|
// monitor coordinates
|
||||||
|
let (ux, uy) = evt.position_transformed(screen_size);
|
||||||
|
((ux + self.current_output_offset(x)) as f64, uy as f64)
|
||||||
} else {
|
} else {
|
||||||
// we are started in winit
|
// we are started in winit
|
||||||
evt.position()
|
evt.position()
|
||||||
|
@ -128,6 +149,64 @@ impl AnvilState {
|
||||||
self.pointer.motion((x, y), under, serial, evt.time());
|
self.pointer.motion((x, y), under, serial, evt.time());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "udev")]
|
||||||
|
fn clamp_coords(&self, pos: (f64, f64)) -> (f64, f64) {
|
||||||
|
let output_map = self.output_map.as_ref().unwrap();
|
||||||
|
let outputs = output_map.borrow();
|
||||||
|
|
||||||
|
if outputs.len() == 0 {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (mut x, mut y) = pos;
|
||||||
|
// max_x is the sum of the width of all outputs
|
||||||
|
let max_x = outputs.iter().fold(0u32, |acc, output| acc + output.size.0);
|
||||||
|
x = x.max(0.0).min(max_x as f64);
|
||||||
|
|
||||||
|
// max y depends on the current output
|
||||||
|
let max_y = self.current_output_size(x).1;
|
||||||
|
y = y.max(0.0).min(max_y as f64);
|
||||||
|
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "udev")]
|
||||||
|
fn current_output_idx(&self, x: f64) -> usize {
|
||||||
|
let output_map = self.output_map.as_ref().unwrap();
|
||||||
|
let outputs = output_map.borrow();
|
||||||
|
|
||||||
|
outputs
|
||||||
|
.iter()
|
||||||
|
// map each output to their x position
|
||||||
|
.scan(0u32, |acc, output| {
|
||||||
|
let curr_x = *acc;
|
||||||
|
*acc += output.size.0;
|
||||||
|
Some(curr_x)
|
||||||
|
})
|
||||||
|
// get an index
|
||||||
|
.enumerate()
|
||||||
|
// find the first one with a greater x
|
||||||
|
.find(|(_idx, x_pos)| *x_pos as f64 > x)
|
||||||
|
// the previous output is the one we are on
|
||||||
|
.map(|(idx, _)| idx - 1)
|
||||||
|
.unwrap_or(outputs.len() - 1)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "udev")]
|
||||||
|
fn current_output_size(&self, x: f64) -> (u32, u32) {
|
||||||
|
let output_map = self.output_map.as_ref().unwrap();
|
||||||
|
let outputs = output_map.borrow();
|
||||||
|
outputs[self.current_output_idx(x)].size
|
||||||
|
}
|
||||||
|
#[cfg(feature = "udev")]
|
||||||
|
fn current_output_offset(&self, x: f64) -> u32 {
|
||||||
|
let output_map = self.output_map.as_ref().unwrap();
|
||||||
|
let outputs = output_map.borrow();
|
||||||
|
outputs
|
||||||
|
.iter()
|
||||||
|
.take(self.current_output_idx(x))
|
||||||
|
.fold(0u32, |acc, output| acc + output.size.0)
|
||||||
|
}
|
||||||
|
|
||||||
fn on_pointer_button<B: InputBackend>(&mut self, evt: B::PointerButtonEvent) {
|
fn on_pointer_button<B: InputBackend>(&mut self, evt: B::PointerButtonEvent) {
|
||||||
let serial = SCOUNTER.next_serial();
|
let serial = SCOUNTER.next_serial();
|
||||||
let button = match evt.button() {
|
let button = match evt.button() {
|
||||||
|
@ -200,6 +279,8 @@ enum KeyAction {
|
||||||
VtSwitch(i32),
|
VtSwitch(i32),
|
||||||
/// run a command
|
/// run a command
|
||||||
Run(String),
|
Run(String),
|
||||||
|
/// Switch the current screen
|
||||||
|
Screen(usize),
|
||||||
/// Forward the key to the client
|
/// Forward the key to the client
|
||||||
Forward,
|
Forward,
|
||||||
/// Do nothing more
|
/// Do nothing more
|
||||||
|
@ -219,6 +300,8 @@ fn process_keyboard_shortcut(modifiers: ModifiersState, keysym: Keysym) -> KeyAc
|
||||||
} else if modifiers.logo && keysym == xkb::KEY_Return {
|
} else if modifiers.logo && keysym == xkb::KEY_Return {
|
||||||
// run terminal
|
// run terminal
|
||||||
KeyAction::Run("weston-terminal".into())
|
KeyAction::Run("weston-terminal".into())
|
||||||
|
} else if modifiers.logo && keysym >= xkb::KEY_1 && keysym <= xkb::KEY_9 {
|
||||||
|
KeyAction::Screen((keysym - xkb::KEY_1) as usize)
|
||||||
} else {
|
} else {
|
||||||
KeyAction::Forward
|
KeyAction::Forward
|
||||||
}
|
}
|
||||||
|
|
|
@ -654,7 +654,7 @@ pub struct CommitedState {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SurfaceData {
|
pub struct SurfaceData {
|
||||||
pub texture: Option<crate::glium_drawer::TextureMetadata>,
|
pub texture: Option<crate::buffer_utils::BufferTextures>,
|
||||||
pub geometry: Option<Rectangle>,
|
pub geometry: Option<Rectangle>,
|
||||||
pub resize_state: ResizeState,
|
pub resize_state: ResizeState,
|
||||||
/// Minimum width and height, as requested by the surface.
|
/// Minimum width and height, as requested by the surface.
|
||||||
|
@ -673,7 +673,7 @@ impl SurfaceData {
|
||||||
/// Apply a next state into the surface current state
|
/// Apply a next state into the surface current state
|
||||||
pub fn apply_state(&mut self, next_state: CommitedState) {
|
pub fn apply_state(&mut self, next_state: CommitedState) {
|
||||||
if Self::merge_state(&mut self.current_state, next_state) {
|
if Self::merge_state(&mut self.current_state, next_state) {
|
||||||
self.texture = None;
|
let _ = self.texture.take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,13 +32,7 @@ pub fn load_shm_buffer(data: BufferData, pool: &[u8]) -> Result<(RawImage2d<'_,
|
||||||
};
|
};
|
||||||
|
|
||||||
// sharders format need to be reversed to account for endianness
|
// sharders format need to be reversed to account for endianness
|
||||||
let (client_format, fragment) = match data.format {
|
let (client_format, fragment) = load_format(data.format)?;
|
||||||
Format::Argb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRA),
|
|
||||||
Format::Xrgb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRX),
|
|
||||||
Format::Rgba8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_ABGR),
|
|
||||||
Format::Rgbx8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_XBGR),
|
|
||||||
_ => return Err(data.format),
|
|
||||||
};
|
|
||||||
Ok((
|
Ok((
|
||||||
RawImage2d {
|
RawImage2d {
|
||||||
data: slice,
|
data: slice,
|
||||||
|
@ -49,3 +43,13 @@ pub fn load_shm_buffer(data: BufferData, pool: &[u8]) -> Result<(RawImage2d<'_,
|
||||||
fragment,
|
fragment,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_format(format: Format) -> Result<(ClientFormat, usize), Format> {
|
||||||
|
Ok(match format {
|
||||||
|
Format::Argb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRA),
|
||||||
|
Format::Xrgb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRX),
|
||||||
|
Format::Rgba8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_ABGR),
|
||||||
|
Format::Rgbx8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_XBGR),
|
||||||
|
_ => return Err(format),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::session::auto::AutoSession,
|
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::{
|
calloop::{
|
||||||
generic::{Fd, Generic},
|
generic::{Fd, Generic},
|
||||||
|
@ -25,8 +24,10 @@ use smithay::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "udev")]
|
#[cfg(feature = "udev")]
|
||||||
use smithay::backend::session::Session;
|
use smithay::backend::session::{auto::AutoSession, Session};
|
||||||
|
|
||||||
|
#[cfg(feature = "udev")]
|
||||||
|
use crate::udev::MyOutput;
|
||||||
use crate::{buffer_utils::BufferUtils, shell::init_shell};
|
use crate::{buffer_utils::BufferUtils, shell::init_shell};
|
||||||
|
|
||||||
pub struct AnvilState {
|
pub struct AnvilState {
|
||||||
|
@ -43,7 +44,8 @@ pub struct AnvilState {
|
||||||
pub keyboard: KeyboardHandle,
|
pub keyboard: KeyboardHandle,
|
||||||
pub pointer_location: Rc<RefCell<(f64, f64)>>,
|
pub pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
pub cursor_status: Arc<Mutex<CursorImageStatus>>,
|
pub cursor_status: Arc<Mutex<CursorImageStatus>>,
|
||||||
pub screen_size: (u32, u32),
|
#[cfg(feature = "udev")]
|
||||||
|
pub output_map: Option<Rc<RefCell<Vec<MyOutput>>>>,
|
||||||
pub seat_name: String,
|
pub seat_name: String,
|
||||||
#[cfg(feature = "udev")]
|
#[cfg(feature = "udev")]
|
||||||
pub session: Option<AutoSession>,
|
pub session: Option<AutoSession>,
|
||||||
|
@ -58,6 +60,8 @@ impl AnvilState {
|
||||||
buffer_utils: BufferUtils,
|
buffer_utils: BufferUtils,
|
||||||
#[cfg(feature = "udev")] session: Option<AutoSession>,
|
#[cfg(feature = "udev")] session: Option<AutoSession>,
|
||||||
#[cfg(not(feature = "udev"))] _session: Option<()>,
|
#[cfg(not(feature = "udev"))] _session: Option<()>,
|
||||||
|
#[cfg(feature = "udev")] output_map: Option<Rc<RefCell<Vec<MyOutput>>>>,
|
||||||
|
#[cfg(not(feature = "udev"))] _output_map: Option<()>,
|
||||||
log: slog::Logger,
|
log: slog::Logger,
|
||||||
) -> AnvilState {
|
) -> AnvilState {
|
||||||
// init the wayland connection
|
// init the wayland connection
|
||||||
|
@ -126,7 +130,7 @@ impl AnvilState {
|
||||||
"anvil".into()
|
"anvil".into()
|
||||||
};
|
};
|
||||||
#[cfg(not(feature = "udev"))]
|
#[cfg(not(feature = "udev"))]
|
||||||
let seat_name = "anvil".into();
|
let seat_name: String = "anvil".into();
|
||||||
|
|
||||||
let (mut seat, _) = Seat::new(
|
let (mut seat, _) = Seat::new(
|
||||||
&mut display.borrow_mut(),
|
&mut display.borrow_mut(),
|
||||||
|
@ -162,7 +166,8 @@ impl AnvilState {
|
||||||
keyboard,
|
keyboard,
|
||||||
cursor_status,
|
cursor_status,
|
||||||
pointer_location: Rc::new(RefCell::new((0.0, 0.0))),
|
pointer_location: Rc::new(RefCell::new((0.0, 0.0))),
|
||||||
screen_size: (1920, 1080),
|
#[cfg(feature = "udev")]
|
||||||
|
output_map,
|
||||||
seat_name,
|
seat_name,
|
||||||
#[cfg(feature = "udev")]
|
#[cfg(feature = "udev")]
|
||||||
session,
|
session,
|
||||||
|
|
|
@ -50,10 +50,11 @@ use smithay::{
|
||||||
nix::{fcntl::OFlag, sys::stat::dev_t},
|
nix::{fcntl::OFlag, sys::stat::dev_t},
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
protocol::{wl_output, wl_surface},
|
protocol::{wl_output, wl_surface},
|
||||||
Display,
|
Display, Global,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
signaling::{Linkable, SignalToken, Signaler},
|
signaling::{Linkable, SignalToken, Signaler},
|
||||||
|
utils::Rectangle,
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::CompositorToken,
|
compositor::CompositorToken,
|
||||||
output::{Mode, Output, PhysicalProperties},
|
output::{Mode, Output, PhysicalProperties},
|
||||||
|
@ -112,6 +113,8 @@ pub fn run_udev(
|
||||||
#[cfg(not(feature = "egl"))]
|
#[cfg(not(feature = "egl"))]
|
||||||
let buffer_utils = BufferUtils::new(log.clone());
|
let buffer_utils = BufferUtils::new(log.clone());
|
||||||
|
|
||||||
|
let output_map = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize session
|
* Initialize session
|
||||||
*/
|
*/
|
||||||
|
@ -124,8 +127,9 @@ pub fn run_udev(
|
||||||
let mut state = AnvilState::init(
|
let mut state = AnvilState::init(
|
||||||
display.clone(),
|
display.clone(),
|
||||||
event_loop.handle(),
|
event_loop.handle(),
|
||||||
buffer_utils,
|
buffer_utils.clone(),
|
||||||
Some(session),
|
Some(session),
|
||||||
|
Some(output_map.clone()),
|
||||||
log.clone(),
|
log.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -139,10 +143,12 @@ pub fn run_udev(
|
||||||
|
|
||||||
let mut udev_handler = UdevHandlerImpl {
|
let mut udev_handler = UdevHandlerImpl {
|
||||||
compositor_token: state.ctoken,
|
compositor_token: state.ctoken,
|
||||||
|
buffer_utils,
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
egl_buffer_reader,
|
egl_buffer_reader,
|
||||||
session: state.session.clone().unwrap(),
|
session: state.session.clone().unwrap(),
|
||||||
backends: HashMap::new(),
|
backends: HashMap::new(),
|
||||||
|
output_map,
|
||||||
display: display.clone(),
|
display: display.clone(),
|
||||||
primary_gpu,
|
primary_gpu,
|
||||||
window_map: state.window_map.clone(),
|
window_map: state.window_map.clone(),
|
||||||
|
@ -158,34 +164,6 @@ pub fn run_udev(
|
||||||
/*
|
/*
|
||||||
* Initialize a fake output (we render one screen to every device in this example)
|
* Initialize a fake output (we render one screen to every device in this example)
|
||||||
*/
|
*/
|
||||||
let (output, _output_global) = Output::new(
|
|
||||||
&mut display.borrow_mut(),
|
|
||||||
"Drm".into(),
|
|
||||||
PhysicalProperties {
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
subpixel: wl_output::Subpixel::Unknown,
|
|
||||||
make: "Smithay".into(),
|
|
||||||
model: "Generic DRM".into(),
|
|
||||||
},
|
|
||||||
log.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let (w, h) = (1920, 1080); // Hardcode full-hd res
|
|
||||||
output.change_current_state(
|
|
||||||
Some(Mode {
|
|
||||||
width: w as i32,
|
|
||||||
height: h as i32,
|
|
||||||
refresh: 60_000,
|
|
||||||
}),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
output.set_preferred(Mode {
|
|
||||||
width: w as i32,
|
|
||||||
height: h as i32,
|
|
||||||
refresh: 60_000,
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize libinput backend
|
* Initialize libinput backend
|
||||||
|
@ -250,6 +228,68 @@ pub fn run_udev(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct MyOutput {
|
||||||
|
pub device_id: dev_t,
|
||||||
|
pub crtc: crtc::Handle,
|
||||||
|
pub size: (u32, u32),
|
||||||
|
_wl: Output,
|
||||||
|
global: Option<Global<wl_output::WlOutput>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyOutput {
|
||||||
|
fn new(
|
||||||
|
display: &mut Display,
|
||||||
|
device_id: dev_t,
|
||||||
|
crtc: crtc::Handle,
|
||||||
|
conn: ConnectorInfo,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
) -> MyOutput {
|
||||||
|
let (output, global) = Output::new(
|
||||||
|
display,
|
||||||
|
format!("{:?}", conn.interface()),
|
||||||
|
PhysicalProperties {
|
||||||
|
width: conn.size().unwrap_or((0, 0)).0 as i32,
|
||||||
|
height: conn.size().unwrap_or((0, 0)).1 as i32,
|
||||||
|
subpixel: wl_output::Subpixel::Unknown,
|
||||||
|
make: "Smithay".into(),
|
||||||
|
model: "Generic DRM".into(),
|
||||||
|
},
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mode = conn.modes()[0];
|
||||||
|
let (w, h) = mode.size();
|
||||||
|
output.change_current_state(
|
||||||
|
Some(Mode {
|
||||||
|
width: w as i32,
|
||||||
|
height: h as i32,
|
||||||
|
refresh: (mode.vrefresh() * 1000) as i32,
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
output.set_preferred(Mode {
|
||||||
|
width: w as i32,
|
||||||
|
height: h as i32,
|
||||||
|
refresh: (mode.vrefresh() * 1000) as i32,
|
||||||
|
});
|
||||||
|
|
||||||
|
MyOutput {
|
||||||
|
device_id,
|
||||||
|
crtc,
|
||||||
|
size: (w as u32, h as u32),
|
||||||
|
_wl: output,
|
||||||
|
global: Some(global),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for MyOutput {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.global.take().unwrap().destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct BackendData {
|
struct BackendData {
|
||||||
_restart_token: SignalToken,
|
_restart_token: SignalToken,
|
||||||
event_source: Source<Generic<RenderDevice>>,
|
event_source: Source<Generic<RenderDevice>>,
|
||||||
|
@ -258,6 +298,7 @@ struct BackendData {
|
||||||
|
|
||||||
struct UdevHandlerImpl<Data: 'static> {
|
struct UdevHandlerImpl<Data: 'static> {
|
||||||
compositor_token: CompositorToken<Roles>,
|
compositor_token: CompositorToken<Roles>,
|
||||||
|
buffer_utils: BufferUtils,
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
||||||
session: AutoSession,
|
session: AutoSession,
|
||||||
|
@ -265,6 +306,7 @@ struct UdevHandlerImpl<Data: 'static> {
|
||||||
display: Rc<RefCell<Display>>,
|
display: Rc<RefCell<Display>>,
|
||||||
primary_gpu: Option<PathBuf>,
|
primary_gpu: Option<PathBuf>,
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
|
output_map: Rc<RefCell<Vec<MyOutput>>>,
|
||||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
|
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||||
cursor_status: Arc<Mutex<CursorImageStatus>>,
|
cursor_status: Arc<Mutex<CursorImageStatus>>,
|
||||||
|
@ -275,10 +317,11 @@ struct UdevHandlerImpl<Data: 'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Data: 'static> UdevHandlerImpl<Data> {
|
impl<Data: 'static> UdevHandlerImpl<Data> {
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
pub fn scan_connectors(
|
pub fn scan_connectors(
|
||||||
device: &mut RenderDevice,
|
device: &mut RenderDevice,
|
||||||
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
buffer_utils: &BufferUtils,
|
||||||
|
display: &mut Display,
|
||||||
|
output_map: &mut Vec<MyOutput>,
|
||||||
logger: &::slog::Logger,
|
logger: &::slog::Logger,
|
||||||
) -> HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>> {
|
) -> HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>> {
|
||||||
// Get a set of all modesetting resource handles (excluding planes):
|
// Get a set of all modesetting resource handles (excluding planes):
|
||||||
|
@ -310,9 +353,16 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
|
||||||
device
|
device
|
||||||
.create_surface(crtc, connector_info.modes()[0], &[connector_info.handle()])
|
.create_surface(crtc, connector_info.modes()[0], &[connector_info.handle()])
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
egl_buffer_reader.clone(),
|
buffer_utils.clone(),
|
||||||
logger.clone(),
|
logger.clone(),
|
||||||
);
|
);
|
||||||
|
output_map.push(MyOutput::new(
|
||||||
|
display,
|
||||||
|
device.device_id(),
|
||||||
|
crtc,
|
||||||
|
connector_info,
|
||||||
|
logger.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
entry.insert(Rc::new(renderer));
|
entry.insert(Rc::new(renderer));
|
||||||
break 'outer;
|
break 'outer;
|
||||||
|
@ -323,53 +373,10 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
|
||||||
|
|
||||||
backends
|
backends
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "egl"))]
|
|
||||||
pub fn scan_connectors(
|
|
||||||
device: &mut RenderDevice,
|
|
||||||
logger: &::slog::Logger,
|
|
||||||
) -> HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>> {
|
|
||||||
// Get a set of all modesetting resource handles (excluding planes):
|
|
||||||
let res_handles = device.resource_handles().unwrap();
|
|
||||||
|
|
||||||
// Use first connected connector
|
|
||||||
let connector_infos: Vec<ConnectorInfo> = res_handles
|
|
||||||
.connectors()
|
|
||||||
.iter()
|
|
||||||
.map(|conn| device.get_connector_info(*conn).unwrap())
|
|
||||||
.filter(|conn| conn.state() == ConnectorState::Connected)
|
|
||||||
.inspect(|conn| info!(logger, "Connected: {:?}", conn.interface()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut backends = HashMap::new();
|
|
||||||
|
|
||||||
// very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
|
|
||||||
for connector_info in connector_infos {
|
|
||||||
let encoder_infos = connector_info
|
|
||||||
.encoders()
|
|
||||||
.iter()
|
|
||||||
.filter_map(|e| *e)
|
|
||||||
.flat_map(|encoder_handle| device.get_encoder_info(encoder_handle))
|
|
||||||
.collect::<Vec<EncoderInfo>>();
|
|
||||||
'outer: for encoder_info in encoder_infos {
|
|
||||||
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
|
||||||
if !backends.contains_key(&crtc) {
|
|
||||||
let renderer =
|
|
||||||
GliumDrawer::init(device.create_surface(crtc).unwrap(), logger.clone());
|
|
||||||
|
|
||||||
backends.insert(crtc, Rc::new(renderer));
|
|
||||||
break 'outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
backends
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Data: 'static> UdevHandlerImpl<Data> {
|
impl<Data: 'static> UdevHandlerImpl<Data> {
|
||||||
fn device_added(&mut self, _device: dev_t, path: PathBuf) {
|
fn device_added(&mut self, device_id: dev_t, path: PathBuf) {
|
||||||
// Try to open the device
|
// Try to open the device
|
||||||
if let Some(mut device) = self
|
if let Some(mut device) = self
|
||||||
.session
|
.session
|
||||||
|
@ -412,21 +419,20 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
{
|
{
|
||||||
if path.canonicalize().ok() == self.primary_gpu {
|
if path.canonicalize().ok() == self.primary_gpu {
|
||||||
|
info!(
|
||||||
|
self.logger,
|
||||||
|
"Initializing EGL Hardware Acceleration via {:?}", path
|
||||||
|
);
|
||||||
*self.egl_buffer_reader.borrow_mut() =
|
*self.egl_buffer_reader.borrow_mut() =
|
||||||
device.bind_wl_display(&*self.display.borrow()).ok();
|
device.bind_wl_display(&*self.display.borrow()).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
let backends = Rc::new(RefCell::new(UdevHandlerImpl::<Data>::scan_connectors(
|
|
||||||
&mut device,
|
|
||||||
self.egl_buffer_reader.clone(),
|
|
||||||
&self.logger,
|
|
||||||
)));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "egl"))]
|
|
||||||
let backends = Rc::new(RefCell::new(UdevHandlerImpl::<Data>::scan_connectors(
|
let backends = Rc::new(RefCell::new(UdevHandlerImpl::<Data>::scan_connectors(
|
||||||
&mut device,
|
&mut device,
|
||||||
|
&self.buffer_utils,
|
||||||
|
&mut *self.display.borrow_mut(),
|
||||||
|
&mut *self.output_map.borrow_mut(),
|
||||||
&self.logger,
|
&self.logger,
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
@ -434,10 +440,13 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
|
||||||
// Note: if you replicate this (very simple) structure, it is rather easy
|
// Note: if you replicate this (very simple) structure, it is rather easy
|
||||||
// to introduce reference cycles with Rc. Be sure about your drop order
|
// to introduce reference cycles with Rc. Be sure about your drop order
|
||||||
let renderer = Rc::new(DrmRenderer {
|
let renderer = Rc::new(DrmRenderer {
|
||||||
|
device_id,
|
||||||
compositor_token: self.compositor_token,
|
compositor_token: self.compositor_token,
|
||||||
backends: backends.clone(),
|
backends: backends.clone(),
|
||||||
window_map: self.window_map.clone(),
|
window_map: self.window_map.clone(),
|
||||||
|
output_map: self.output_map.clone(),
|
||||||
pointer_location: self.pointer_location.clone(),
|
pointer_location: self.pointer_location.clone(),
|
||||||
|
pointer_image: self.pointer_image.clone(),
|
||||||
cursor_status: self.cursor_status.clone(),
|
cursor_status: self.cursor_status.clone(),
|
||||||
dnd_icon: self.dnd_icon.clone(),
|
dnd_icon: self.dnd_icon.clone(),
|
||||||
logger: self.logger.clone(),
|
logger: self.logger.clone(),
|
||||||
|
@ -462,12 +471,6 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
for renderer in backends.borrow_mut().values() {
|
for renderer in backends.borrow_mut().values() {
|
||||||
// create cursor
|
|
||||||
renderer
|
|
||||||
.borrow()
|
|
||||||
.set_cursor_representation(&self.pointer_image, (2, 2))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// render first frame
|
// render first frame
|
||||||
schedule_initial_render(renderer.clone(), &self.loop_handle);
|
schedule_initial_render(renderer.clone(), &self.loop_handle);
|
||||||
}
|
}
|
||||||
|
@ -485,28 +488,25 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
|
||||||
|
|
||||||
fn device_changed(&mut self, device: dev_t) {
|
fn device_changed(&mut self, device: dev_t) {
|
||||||
//quick and dirty, just re-init all backends
|
//quick and dirty, just re-init all backends
|
||||||
|
let buffer_utils = &self.buffer_utils;
|
||||||
if let Some(ref mut backend_data) = self.backends.get_mut(&device) {
|
if let Some(ref mut backend_data) = self.backends.get_mut(&device) {
|
||||||
let logger = &self.logger;
|
let logger = &self.logger;
|
||||||
let pointer_image = &self.pointer_image;
|
|
||||||
let egl_buffer_reader = self.egl_buffer_reader.clone();
|
|
||||||
let loop_handle = self.loop_handle.clone();
|
let loop_handle = self.loop_handle.clone();
|
||||||
|
let mut display = self.display.borrow_mut();
|
||||||
|
let mut output_map = self.output_map.borrow_mut();
|
||||||
|
output_map.retain(|output| output.device_id != device);
|
||||||
self.loop_handle
|
self.loop_handle
|
||||||
.with_source(&backend_data.event_source, |source| {
|
.with_source(&backend_data.event_source, |source| {
|
||||||
let mut backends = backend_data.surfaces.borrow_mut();
|
let mut backends = backend_data.surfaces.borrow_mut();
|
||||||
#[cfg(feature = "egl")]
|
*backends = UdevHandlerImpl::<Data>::scan_connectors(
|
||||||
let new_backends =
|
&mut source.file,
|
||||||
UdevHandlerImpl::<Data>::scan_connectors(&mut source.file, egl_buffer_reader, logger);
|
buffer_utils,
|
||||||
#[cfg(not(feature = "egl"))]
|
&mut *display,
|
||||||
let new_backends = UdevHandlerImpl::<Data>::scan_connectors(&mut source.file, logger);
|
&mut *output_map,
|
||||||
*backends = new_backends;
|
logger,
|
||||||
|
);
|
||||||
|
|
||||||
for renderer in backends.values() {
|
for renderer in backends.values() {
|
||||||
// create cursor
|
|
||||||
renderer
|
|
||||||
.borrow()
|
|
||||||
.set_cursor_representation(pointer_image, (2, 2))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// render first frame
|
// render first frame
|
||||||
schedule_initial_render(renderer.clone(), &loop_handle);
|
schedule_initial_render(renderer.clone(), &loop_handle);
|
||||||
}
|
}
|
||||||
|
@ -520,6 +520,10 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
|
||||||
// drop surfaces
|
// drop surfaces
|
||||||
backend_data.surfaces.borrow_mut().clear();
|
backend_data.surfaces.borrow_mut().clear();
|
||||||
debug!(self.logger, "Surfaces dropped");
|
debug!(self.logger, "Surfaces dropped");
|
||||||
|
// clear outputs
|
||||||
|
self.output_map
|
||||||
|
.borrow_mut()
|
||||||
|
.retain(|output| output.device_id != device);
|
||||||
|
|
||||||
let device = self.loop_handle.remove(backend_data.event_source).unwrap();
|
let device = self.loop_handle.remove(backend_data.event_source).unwrap();
|
||||||
|
|
||||||
|
@ -568,10 +572,13 @@ impl<Data: 'static> DrmRendererSessionListener<Data> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DrmRenderer {
|
pub struct DrmRenderer {
|
||||||
|
device_id: dev_t,
|
||||||
compositor_token: CompositorToken<Roles>,
|
compositor_token: CompositorToken<Roles>,
|
||||||
backends: Rc<RefCell<HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>>>>,
|
backends: Rc<RefCell<HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>>>>,
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
|
output_map: Rc<RefCell<Vec<MyOutput>>>,
|
||||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
|
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||||
cursor_status: Arc<Mutex<CursorImageStatus>>,
|
cursor_status: Arc<Mutex<CursorImageStatus>>,
|
||||||
dnd_icon: Arc<Mutex<Option<wl_surface::WlSurface>>>,
|
dnd_icon: Arc<Mutex<Option<wl_surface::WlSurface>>>,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
|
@ -590,30 +597,52 @@ impl DrmRenderer {
|
||||||
evt_handle: Option<&LoopHandle<Data>>,
|
evt_handle: Option<&LoopHandle<Data>>,
|
||||||
) {
|
) {
|
||||||
if let Some(drawer) = self.backends.borrow().get(&crtc) {
|
if let Some(drawer) = self.backends.borrow().get(&crtc) {
|
||||||
{
|
// get output coordinates
|
||||||
let (x, y) = *self.pointer_location.borrow();
|
let (x, y) = self
|
||||||
let _ = drawer
|
.output_map
|
||||||
.borrow()
|
.borrow()
|
||||||
.set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
|
.iter()
|
||||||
}
|
.take_while(|output| output.device_id != self.device_id || output.crtc != crtc)
|
||||||
|
.fold((0u32, 0u32), |pos, output| (pos.0 + output.size.0, pos.1));
|
||||||
|
let (width, height) = self
|
||||||
|
.output_map
|
||||||
|
.borrow()
|
||||||
|
.iter()
|
||||||
|
.find(|output| output.device_id == self.device_id && output.crtc == crtc)
|
||||||
|
.map(|output| output.size)
|
||||||
|
.unwrap_or((0, 0)); // in this case the output will be removed.
|
||||||
|
|
||||||
// and draw in sync with our monitor
|
// and draw in sync with our monitor
|
||||||
let mut frame = drawer.draw();
|
let mut frame = drawer.draw();
|
||||||
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
|
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
|
||||||
// draw the surfaces
|
// draw the surfaces
|
||||||
drawer.draw_windows(&mut frame, &*self.window_map.borrow(), self.compositor_token);
|
drawer.draw_windows(
|
||||||
let (x, y) = *self.pointer_location.borrow();
|
&mut frame,
|
||||||
|
&*self.window_map.borrow(),
|
||||||
|
Some(Rectangle {
|
||||||
|
x: x as i32,
|
||||||
|
y: y as i32,
|
||||||
|
width: width as i32,
|
||||||
|
height: height as i32,
|
||||||
|
}),
|
||||||
|
self.compositor_token,
|
||||||
|
);
|
||||||
|
|
||||||
|
// get pointer coordinates
|
||||||
|
let (ptr_x, ptr_y) = *self.pointer_location.borrow();
|
||||||
|
let ptr_x = ptr_x.trunc().abs() as i32 - x as i32;
|
||||||
|
let ptr_y = ptr_y.trunc().abs() as i32 - y as i32;
|
||||||
|
|
||||||
|
// set cursor
|
||||||
|
if ptr_x >= 0 && ptr_x < width as i32 && ptr_y >= 0 && ptr_y < height as i32 {
|
||||||
|
let _ = drawer.borrow().set_cursor_position(ptr_x as u32, ptr_y as u32);
|
||||||
|
|
||||||
// draw the dnd icon if applicable
|
// draw the dnd icon if applicable
|
||||||
{
|
{
|
||||||
let guard = self.dnd_icon.lock().unwrap();
|
let guard = self.dnd_icon.lock().unwrap();
|
||||||
if let Some(ref surface) = *guard {
|
if let Some(ref surface) = *guard {
|
||||||
if surface.as_ref().is_alive() {
|
if surface.as_ref().is_alive() {
|
||||||
drawer.draw_dnd_icon(
|
drawer.draw_dnd_icon(&mut frame, surface, (ptr_x, ptr_y), self.compositor_token);
|
||||||
&mut frame,
|
|
||||||
surface,
|
|
||||||
(x as i32, y as i32),
|
|
||||||
self.compositor_token,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -629,17 +658,21 @@ impl DrmRenderer {
|
||||||
*guard = CursorImageStatus::Default;
|
*guard = CursorImageStatus::Default;
|
||||||
}
|
}
|
||||||
if let CursorImageStatus::Image(ref surface) = *guard {
|
if let CursorImageStatus::Image(ref surface) = *guard {
|
||||||
drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), self.compositor_token);
|
drawer.draw_software_cursor(
|
||||||
|
&mut frame,
|
||||||
|
surface,
|
||||||
|
(ptr_x, ptr_y),
|
||||||
|
self.compositor_token,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
drawer.draw_hardware_cursor(&self.pointer_image, (2, 2), (ptr_x, ptr_y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
drawer.clear_cursor();
|
||||||
|
}
|
||||||
|
|
||||||
let result = frame.finish();
|
if let Err(err) = frame.finish() {
|
||||||
if result.is_ok() {
|
|
||||||
// Send frame events so that client start drawing their next frame
|
|
||||||
self.window_map.borrow().send_frames(SCOUNTER.next_serial());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(err) = result {
|
|
||||||
warn!(self.logger, "Error during rendering: {:?}", err);
|
warn!(self.logger, "Error during rendering: {:?}", err);
|
||||||
let reschedule = match err {
|
let reschedule = match err {
|
||||||
SwapBuffersError::AlreadySwapped => false,
|
SwapBuffersError::AlreadySwapped => false,
|
||||||
|
@ -692,6 +725,7 @@ impl DrmRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: only send drawn windows the frames callback
|
||||||
// Send frame events so that client start drawing their next frame
|
// Send frame events so that client start drawing their next frame
|
||||||
self.window_map.borrow().send_frames(SCOUNTER.next_serial());
|
self.window_map.borrow().send_frames(SCOUNTER.next_serial());
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,10 +248,10 @@ where
|
||||||
|
|
||||||
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
|
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
|
||||||
where
|
where
|
||||||
Func: FnMut(&Kind<R>, (i32, i32)),
|
Func: FnMut(&Kind<R>, (i32, i32), &Rectangle),
|
||||||
{
|
{
|
||||||
for w in self.windows.iter().rev() {
|
for w in self.windows.iter().rev() {
|
||||||
f(&w.toplevel, w.location)
|
f(&w.toplevel, w.location, &w.bbox)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};
|
use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};
|
||||||
|
|
||||||
|
#[cfg(feature = "egl")]
|
||||||
|
use smithay::backend::egl::EGLGraphicsBackend;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{egl::EGLGraphicsBackend, graphics::gl::GLGraphicsBackend, input::InputBackend, winit},
|
backend::{graphics::gl::GLGraphicsBackend, input::InputBackend, winit},
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::EventLoop,
|
calloop::EventLoop,
|
||||||
wayland_server::{protocol::wl_output, Display},
|
wayland_server::{protocol::wl_output, Display},
|
||||||
|
winit::window::CursorIcon,
|
||||||
},
|
},
|
||||||
wayland::{
|
wayland::{
|
||||||
output::{Mode, Output, PhysicalProperties},
|
output::{Mode, Output, PhysicalProperties},
|
||||||
|
@ -38,17 +41,14 @@ pub fn run_winit(
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
let (w, h) = renderer.get_framebuffer_dimensions();
|
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
let drawer = GliumDrawer::init(renderer, egl_buffer_reader.clone(), log.clone());
|
|
||||||
#[cfg(not(feature = "egl"))]
|
|
||||||
let drawer = GliumDrawer::init(renderer, log.clone());
|
|
||||||
|
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone());
|
let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone());
|
||||||
#[cfg(not(feature = "egl"))]
|
#[cfg(not(feature = "egl"))]
|
||||||
let buffer_utils = BufferUtils::new(log.clone());
|
let buffer_utils = BufferUtils::new(log.clone());
|
||||||
|
|
||||||
|
let (w, h) = renderer.get_framebuffer_dimensions();
|
||||||
|
let drawer = GliumDrawer::init(renderer, buffer_utils.clone(), log.clone());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the globals
|
* Initialize the globals
|
||||||
*/
|
*/
|
||||||
|
@ -58,6 +58,7 @@ pub fn run_winit(
|
||||||
event_loop.handle(),
|
event_loop.handle(),
|
||||||
buffer_utils,
|
buffer_utils,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
log.clone(),
|
log.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -103,7 +104,7 @@ pub fn run_winit(
|
||||||
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
|
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
|
||||||
|
|
||||||
// draw the windows
|
// draw the windows
|
||||||
drawer.draw_windows(&mut frame, &*state.window_map.borrow(), state.ctoken);
|
drawer.draw_windows(&mut frame, &*state.window_map.borrow(), None, state.ctoken);
|
||||||
|
|
||||||
let (x, y) = *state.pointer_location.borrow();
|
let (x, y) = *state.pointer_location.borrow();
|
||||||
// draw the dnd icon if any
|
// draw the dnd icon if any
|
||||||
|
@ -128,7 +129,9 @@ pub fn run_winit(
|
||||||
}
|
}
|
||||||
// draw as relevant
|
// draw as relevant
|
||||||
if let CursorImageStatus::Image(ref surface) = *guard {
|
if let CursorImageStatus::Image(ref surface) = *guard {
|
||||||
drawer.draw_cursor(&mut frame, surface, (x as i32, y as i32), state.ctoken);
|
drawer.draw_software_cursor(&mut frame, surface, (x as i32, y as i32), state.ctoken);
|
||||||
|
} else {
|
||||||
|
drawer.draw_hardware_cursor(&CursorIcon::Default, (0, 0), (x as i32, y as i32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -646,6 +646,15 @@ impl<A: AsRawFd + 'static> CursorBackend for AtomicDrmSurfaceInternal<A> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_cursor_representation(&self) -> Result<(), Self::Error> {
|
||||||
|
let mut cursor = self.cursor.lock().unwrap();
|
||||||
|
if let Some(fb) = cursor.framebuffer.take() {
|
||||||
|
let _ = self.destroy_framebuffer(fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.clear_plane(self.planes.cursor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
|
impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
|
||||||
|
@ -1022,6 +1031,10 @@ impl<A: AsRawFd + 'static> CursorBackend for AtomicDrmSurface<A> {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.0.set_cursor_representation(buffer, hotspot)
|
self.0.set_cursor_representation(buffer, hotspot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_cursor_representation(&self) -> Result<(), Self::Error> {
|
||||||
|
self.0.clear_cursor_representation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: AsRawFd + 'static> Surface for AtomicDrmSurface<A> {
|
impl<A: AsRawFd + 'static> Surface for AtomicDrmSurface<A> {
|
||||||
|
|
|
@ -552,6 +552,7 @@ where
|
||||||
|
|
||||||
fallback_surface_err_impl!(set_cursor_position, &Self, Result<(), EitherError<E3, E4>>, x: u32, y: u32);
|
fallback_surface_err_impl!(set_cursor_position, &Self, Result<(), EitherError<E3, E4>>, x: u32, y: u32);
|
||||||
fallback_surface_err_impl!(set_cursor_representation, &Self, Result<(), EitherError<E3, E4>>, buffer: &Self::CursorFormat, hotspot: (u32, u32));
|
fallback_surface_err_impl!(set_cursor_representation, &Self, Result<(), EitherError<E3, E4>>, buffer: &Self::CursorFormat, hotspot: (u32, u32));
|
||||||
|
fallback_surface_err_impl!(clear_cursor_representation, &Self, Result<(), EitherError<E3, E4>>,);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "renderer_gl")]
|
#[cfg(feature = "renderer_gl")]
|
||||||
|
|
|
@ -92,6 +92,10 @@ where
|
||||||
) -> ::std::result::Result<(), Self::Error> {
|
) -> ::std::result::Result<(), Self::Error> {
|
||||||
self.0.surface.set_cursor_representation(buffer, hotspot)
|
self.0.surface.set_cursor_representation(buffer, hotspot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_cursor_representation(&self) -> Result<(), Self::Error> {
|
||||||
|
self.0.surface.clear_cursor_representation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "renderer_gl")]
|
#[cfg(feature = "renderer_gl")]
|
||||||
|
|
|
@ -190,6 +190,18 @@ impl<S: RawSurface + 'static> CursorBackend for EglStreamSurfaceInternal<S> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_cursor_representation(&self) -> Result<(), Self::Error> {
|
||||||
|
self.crtc
|
||||||
|
.set_cursor(self.crtc.crtc(), Option::<&DumbBuffer>::None)
|
||||||
|
.compat()
|
||||||
|
.map_err(|source| DrmError::Access {
|
||||||
|
errmsg: "Failed to clear cursor",
|
||||||
|
dev: self.crtc.dev_path(),
|
||||||
|
source,
|
||||||
|
})
|
||||||
|
.map_err(Error::Underlying)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// egl stream surface for rendering
|
/// egl stream surface for rendering
|
||||||
|
@ -565,6 +577,10 @@ impl<S: RawSurface + 'static> CursorBackend for EglStreamSurface<S> {
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.0.set_cursor_representation(buffer, hotspot)
|
self.0.set_cursor_representation(buffer, hotspot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_cursor_representation(&self) -> Result<(), Self::Error> {
|
||||||
|
self.0.clear_cursor_representation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -305,6 +305,23 @@ where
|
||||||
*self.cursor.lock().unwrap() = (cursor, hotspot);
|
*self.cursor.lock().unwrap() = (cursor, hotspot);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_cursor_representation(&self) -> Result<(), Self::Error> {
|
||||||
|
*self.cursor.lock().unwrap() = (
|
||||||
|
self.dev
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.create_buffer_object(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
GbmFormat::ARGB8888,
|
||||||
|
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
||||||
|
)
|
||||||
|
.map_err(Error::BufferCreationFailed)?,
|
||||||
|
(0, 0),
|
||||||
|
);
|
||||||
|
self.crtc.clear_cursor_representation().map_err(Error::Underlying)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: RawSurface + 'static> Drop for GbmSurfaceInternal<S> {
|
impl<S: RawSurface + 'static> Drop for GbmSurfaceInternal<S> {
|
||||||
|
@ -415,6 +432,10 @@ where
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.0.set_cursor_representation(buffer, hotspot)
|
self.0.set_cursor_representation(buffer, hotspot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_cursor_representation(&self) -> Result<(), Self::Error> {
|
||||||
|
self.0.clear_cursor_representation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -87,6 +87,16 @@ impl<A: AsRawFd + 'static> CursorBackend for LegacyDrmSurfaceInternal<A> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_cursor_representation(&self) -> Result<(), Error> {
|
||||||
|
self.set_cursor(self.crtc, Option::<&DumbBuffer>::None)
|
||||||
|
.compat()
|
||||||
|
.map_err(|source| Error::Access {
|
||||||
|
errmsg: "Failed to set cursor",
|
||||||
|
dev: self.dev_path(),
|
||||||
|
source,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: AsRawFd + 'static> Surface for LegacyDrmSurfaceInternal<A> {
|
impl<A: AsRawFd + 'static> Surface for LegacyDrmSurfaceInternal<A> {
|
||||||
|
@ -498,6 +508,10 @@ impl<A: AsRawFd + 'static> CursorBackend for LegacyDrmSurface<A> {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.0.set_cursor_representation(buffer, hotspot)
|
self.0.set_cursor_representation(buffer, hotspot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_cursor_representation(&self) -> Result<(), Self::Error> {
|
||||||
|
self.0.clear_cursor_representation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: AsRawFd + 'static> Surface for LegacyDrmSurface<A> {
|
impl<A: AsRawFd + 'static> Surface for LegacyDrmSurface<A> {
|
||||||
|
|
|
@ -475,7 +475,7 @@ impl EGLBufferReader {
|
||||||
/// to render it another way.
|
/// to render it another way.
|
||||||
pub fn egl_buffer_contents(
|
pub fn egl_buffer_contents(
|
||||||
&self,
|
&self,
|
||||||
buffer: WlBuffer,
|
buffer: &WlBuffer,
|
||||||
) -> ::std::result::Result<EGLImages, BufferAccessError> {
|
) -> ::std::result::Result<EGLImages, BufferAccessError> {
|
||||||
let mut format: i32 = 0;
|
let mut format: i32 = 0;
|
||||||
let query = wrap_egl_call(|| unsafe {
|
let query = wrap_egl_call(|| unsafe {
|
||||||
|
@ -486,9 +486,9 @@ impl EGLBufferReader {
|
||||||
&mut format,
|
&mut format,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?;
|
.map_err(BufferAccessError::NotManaged)?;
|
||||||
if query == ffi::egl::FALSE {
|
if query == ffi::egl::FALSE {
|
||||||
return Err(BufferAccessError::NotManaged(buffer, EGLError::BadParameter));
|
return Err(BufferAccessError::NotManaged(EGLError::BadParameter));
|
||||||
}
|
}
|
||||||
|
|
||||||
let format = match format {
|
let format = match format {
|
||||||
|
@ -510,7 +510,7 @@ impl EGLBufferReader {
|
||||||
&mut width,
|
&mut width,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?;
|
.map_err(BufferAccessError::NotManaged)?;
|
||||||
|
|
||||||
let mut height: i32 = 0;
|
let mut height: i32 = 0;
|
||||||
wrap_egl_call(|| unsafe {
|
wrap_egl_call(|| unsafe {
|
||||||
|
@ -521,7 +521,7 @@ impl EGLBufferReader {
|
||||||
&mut height,
|
&mut height,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?;
|
.map_err(BufferAccessError::NotManaged)?;
|
||||||
|
|
||||||
let mut inverted: i32 = 0;
|
let mut inverted: i32 = 0;
|
||||||
wrap_egl_call(|| unsafe {
|
wrap_egl_call(|| unsafe {
|
||||||
|
@ -532,7 +532,7 @@ impl EGLBufferReader {
|
||||||
&mut inverted,
|
&mut inverted,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?;
|
.map_err(BufferAccessError::NotManaged)?;
|
||||||
|
|
||||||
let mut images = Vec::with_capacity(format.num_planes());
|
let mut images = Vec::with_capacity(format.num_planes());
|
||||||
for i in 0..format.num_planes() {
|
for i in 0..format.num_planes() {
|
||||||
|
@ -562,7 +562,6 @@ impl EGLBufferReader {
|
||||||
y_inverted: inverted != 0,
|
y_inverted: inverted != 0,
|
||||||
format,
|
format,
|
||||||
images,
|
images,
|
||||||
buffer,
|
|
||||||
#[cfg(feature = "renderer_gl")]
|
#[cfg(feature = "renderer_gl")]
|
||||||
gl: self.gl.clone(),
|
gl: self.gl.clone(),
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,7 +26,7 @@ use crate::backend::graphics::{
|
||||||
use nix::libc::c_uint;
|
use nix::libc::c_uint;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
use wayland_server::{protocol::wl_buffer::WlBuffer, Display};
|
use wayland_server::Display;
|
||||||
|
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub use self::context::EGLContext;
|
pub use self::context::EGLContext;
|
||||||
|
@ -87,8 +87,8 @@ pub enum BufferAccessError {
|
||||||
#[error("The corresponding context was lost")]
|
#[error("The corresponding context was lost")]
|
||||||
ContextLost,
|
ContextLost,
|
||||||
/// This buffer is not managed by the EGL buffer
|
/// This buffer is not managed by the EGL buffer
|
||||||
#[error("This buffer is not managed by EGL. Err: {1:}")]
|
#[error("This buffer is not managed by EGL. Err: {0:}")]
|
||||||
NotManaged(WlBuffer, #[source] EGLError),
|
NotManaged(#[source] EGLError),
|
||||||
/// Failed to create `EGLImages` from the buffer
|
/// Failed to create `EGLImages` from the buffer
|
||||||
#[error("Failed to create EGLImages from the buffer. Err: {0:}")]
|
#[error("Failed to create EGLImages from the buffer. Err: {0:}")]
|
||||||
EGLImageCreationFailed(#[source] EGLError),
|
EGLImageCreationFailed(#[source] EGLError),
|
||||||
|
@ -102,7 +102,7 @@ impl fmt::Debug for BufferAccessError {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"),
|
BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"),
|
||||||
BufferAccessError::NotManaged(_, _) => write!(formatter, "BufferAccessError::NotManaged"),
|
BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"),
|
||||||
BufferAccessError::EGLImageCreationFailed(_) => {
|
BufferAccessError::EGLImageCreationFailed(_) => {
|
||||||
write!(formatter, "BufferAccessError::EGLImageCreationFailed")
|
write!(formatter, "BufferAccessError::EGLImageCreationFailed")
|
||||||
}
|
}
|
||||||
|
@ -265,7 +265,6 @@ pub struct EGLImages {
|
||||||
/// Format of these images
|
/// Format of these images
|
||||||
pub format: Format,
|
pub format: Format,
|
||||||
images: Vec<EGLImage>,
|
images: Vec<EGLImage>,
|
||||||
buffer: WlBuffer,
|
|
||||||
#[cfg(feature = "renderer_gl")]
|
#[cfg(feature = "renderer_gl")]
|
||||||
gl: gl_ffi::Gles2,
|
gl: gl_ffi::Gles2,
|
||||||
}
|
}
|
||||||
|
@ -339,7 +338,6 @@ impl Drop for EGLImages {
|
||||||
ffi::egl::DestroyImageKHR(**self.display, image);
|
ffi::egl::DestroyImageKHR(**self.display, image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.buffer.release();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,4 +34,7 @@ pub trait CursorBackend {
|
||||||
cursor: &Self::CursorFormat,
|
cursor: &Self::CursorFormat,
|
||||||
hotspot: (u32, u32),
|
hotspot: (u32, u32),
|
||||||
) -> Result<(), Self::Error>;
|
) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
|
/// Clear the current cursor image drawn on the [`CursorBackend`].
|
||||||
|
fn clear_cursor_representation(&self) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,9 @@ impl AsErrno for () {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod auto;
|
pub mod auto;
|
||||||
mod dbus;
|
|
||||||
pub mod direct;
|
pub mod direct;
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
mod dbus;
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
pub use self::dbus::*;
|
pub use self::dbus::*;
|
||||||
|
|
|
@ -270,6 +270,12 @@ impl CursorBackend for WinitGraphicsBackend {
|
||||||
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be
|
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be
|
||||||
debug!(self.logger, "Changing cursor representation");
|
debug!(self.logger, "Changing cursor representation");
|
||||||
self.window.window().set_cursor_icon(*cursor);
|
self.window.window().set_cursor_icon(*cursor);
|
||||||
|
self.window.window().set_cursor_visible(true);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_cursor_representation(&self) -> ::std::result::Result<(), ()> {
|
||||||
|
self.window.window().set_cursor_visible(false);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,3 +21,5 @@ pub use wayland_commons;
|
||||||
pub use wayland_protocols;
|
pub use wayland_protocols;
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
pub use wayland_server;
|
pub use wayland_server;
|
||||||
|
#[cfg(feature = "backend_winit")]
|
||||||
|
pub use winit;
|
||||||
|
|
|
@ -17,4 +17,20 @@ impl Rectangle {
|
||||||
let (x, y) = point;
|
let (x, y) = point;
|
||||||
(x >= self.x) && (x < self.x + self.width) && (y >= self.y) && (y < self.y + self.height)
|
(x >= self.x) && (x < self.x + self.width) && (y >= self.y) && (y < self.y + self.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether a given rectangle overlaps with this one
|
||||||
|
pub fn overlaps(&self, other: &Rectangle) -> bool {
|
||||||
|
// if the rectangle is not outside of the other
|
||||||
|
// they must overlap
|
||||||
|
!(
|
||||||
|
// self is left of other
|
||||||
|
self.x + self.width < other.x
|
||||||
|
// self is right of other
|
||||||
|
|| self.x > other.x + other.width
|
||||||
|
// self is above of other
|
||||||
|
|| self.y + self.height < other.y
|
||||||
|
// self is below of other
|
||||||
|
|| self.y > other.y + other.height
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue