diff --git a/anvil/src/buffer_utils.rs b/anvil/src/buffer_utils.rs new file mode 100644 index 0000000..c568d9d --- /dev/null +++ b/anvil/src/buffer_utils.rs @@ -0,0 +1,59 @@ +use std::{cell::RefCell, rc::Rc}; + +use slog::Logger; + +#[cfg(feature = "egl")] +use smithay::backend::egl::{BufferAccessError, EGLDisplay}; +use smithay::{ + reexports::wayland_server::protocol::wl_buffer::WlBuffer, + wayland::shm::with_buffer_contents as shm_buffer_contents, +}; + +/// Utilities for working with `WlBuffer`s. +#[derive(Clone)] +pub struct BufferUtils { + #[cfg(feature = "egl")] + egl_display: Rc>>, + log: Logger, +} + +impl BufferUtils { + /// Creates a new `BufferUtils`. + #[cfg(feature = "egl")] + pub fn new(egl_display: Rc>>, log: Logger) -> Self { + Self { egl_display, log } + } + + /// Creates a new `BufferUtils`. + #[cfg(not(feature = "egl"))] + pub fn new(log: Logger) -> Self { + Self { log } + } + + /// Returns the dimensions of an image stored in the buffer. + #[cfg(feature = "egl")] + pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> { + // Try to retrieve the EGL dimensions of this buffer, and, if that fails, the shm dimensions. + self.egl_display + .borrow() + .as_ref() + .and_then(|display| display.egl_buffer_dimensions(buffer)) + .or_else(|| self.shm_buffer_dimensions(buffer)) + } + + /// Returns the dimensions of an image stored in the buffer. + #[cfg(not(feature = "egl"))] + pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> { + self.shm_buffer_dimensions(buffer) + } + + /// Returns the dimensions of an image stored in the shm buffer. + fn shm_buffer_dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> { + shm_buffer_contents(buffer, |_, data| (data.width, data.height)) + .map_err(|err| { + warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err)); + err + }) + .ok() + } +} diff --git a/anvil/src/main.rs b/anvil/src/main.rs index 079802e..f593091 100644 --- a/anvil/src/main.rs +++ b/anvil/src/main.rs @@ -12,6 +12,7 @@ use smithay::reexports::{calloop::EventLoop, wayland_server::Display}; #[macro_use] mod shaders; +mod buffer_utils; mod glium_drawer; mod input_handler; mod shell; diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index b9fa6a3..26a7378 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -29,7 +29,10 @@ use smithay::{ }, }; -use crate::window_map::{Kind as SurfaceKind, WindowMap}; +use crate::{ + buffer_utils::BufferUtils, + window_map::{Kind as SurfaceKind, WindowMap}, +}; define_roles!(Roles => [ XdgSurface, XdgSurfaceRole ] @@ -98,6 +101,7 @@ impl PointerGrab for MoveSurfaceGrab { pub fn init_shell( display: &mut Display, + buffer_utils: BufferUtils, log: ::slog::Logger, ) -> ( CompositorToken, @@ -109,7 +113,7 @@ pub fn init_shell( let (compositor_token, _, _) = compositor_init( display, move |request, surface, ctoken| match request { - SurfaceEvent::Commit => surface_commit(&surface, ctoken), + SurfaceEvent::Commit => surface_commit(&surface, ctoken, &buffer_utils), SurfaceEvent::Frame { callback } => callback .implement_closure(|_, _| unreachable!(), None::, ()) .done(0), @@ -273,10 +277,15 @@ pub fn init_shell( pub struct SurfaceData { pub buffer: Option, pub texture: Option, + pub dimensions: Option<(i32, i32)>, pub input_region: Option, } -fn surface_commit(surface: &wl_surface::WlSurface, token: CompositorToken) { +fn surface_commit( + surface: &wl_surface::WlSurface, + token: CompositorToken, + buffer_utils: &BufferUtils, +) { token.with_surface_data(surface, |attributes| { attributes.user_data.insert_if_missing(SurfaceData::default); let data = attributes.user_data.get_mut::().unwrap(); @@ -292,6 +301,8 @@ fn surface_commit(surface: &wl_surface::WlSurface, token: CompositorToken old_buffer.release(); } data.texture = None; + // If this fails, the buffer will be discarded later by the drawing code. + data.dimensions = buffer_utils.dimensions(data.buffer.as_ref().unwrap()); } Some(None) => { // erase the contents @@ -299,6 +310,7 @@ fn surface_commit(surface: &wl_surface::WlSurface, token: CompositorToken old_buffer.release(); } data.texture = None; + data.dimensions = None; } None => {} } @@ -306,12 +318,10 @@ fn surface_commit(surface: &wl_surface::WlSurface, token: CompositorToken } fn get_size(attrs: &SurfaceAttributes) -> Option<(i32, i32)> { - attrs.user_data.get::().and_then(|data| { - data.texture - .as_ref() - .map(|ref meta| meta.dimensions) - .map(|(x, y)| (x as i32, y as i32)) - }) + attrs + .user_data + .get::() + .and_then(|data| data.dimensions) } fn contains_point(attrs: &SurfaceAttributes, point: (f64, f64)) -> bool { diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index 24ddb21..c3b3710 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -61,6 +61,7 @@ use smithay::{ }, }; +use crate::buffer_utils::BufferUtils; use crate::glium_drawer::GliumDrawer; use crate::input_handler::AnvilInputHandler; use crate::shell::{init_shell, MyWindowMap, Roles}; @@ -85,6 +86,11 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger #[cfg(feature = "egl")] let active_egl_context = Rc::new(RefCell::new(None)); + #[cfg(feature = "egl")] + let buffer_utils = BufferUtils::new(active_egl_context.clone(), log.clone()); + #[cfg(not(feature = "egl"))] + let buffer_utils = BufferUtils::new(log.clone()); + let display = Rc::new(RefCell::new(display)); /* @@ -92,7 +98,8 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger */ init_shm_global(&mut display.borrow_mut(), vec![], log.clone()); - let (compositor_token, _, _, window_map) = init_shell(&mut display.borrow_mut(), log.clone()); + let (compositor_token, _, _, window_map) = + init_shell(&mut display.borrow_mut(), buffer_utils, log.clone()); /* * Initialize session diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index 48ceb7e..841936d 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -23,6 +23,7 @@ use smithay::{ use slog::Logger; +use crate::buffer_utils::BufferUtils; use crate::glium_drawer::GliumDrawer; use crate::input_handler::AnvilInputHandler; use crate::shell::init_shell; @@ -42,10 +43,15 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log let (w, h) = renderer.get_framebuffer_dimensions(); #[cfg(feature = "egl")] - let drawer = GliumDrawer::init(renderer, egl_display, log.clone()); + let drawer = GliumDrawer::init(renderer, egl_display.clone(), log.clone()); #[cfg(not(feature = "egl"))] let drawer = GliumDrawer::init(renderer, log.clone()); + #[cfg(feature = "egl")] + let buffer_utils = BufferUtils::new(egl_display, log.clone()); + #[cfg(not(feature = "egl"))] + let buffer_utils = BufferUtils::new(log.clone()); + let name = display.add_socket_auto().unwrap().into_string().unwrap(); info!(log, "Listening on wayland socket"; "name" => name.clone()); ::std::env::set_var("WAYLAND_DISPLAY", name); @@ -58,7 +64,7 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log init_shm_global(display, vec![], log.clone()); - let (compositor_token, _, _, window_map) = init_shell(display, log.clone()); + let (compositor_token, _, _, window_map) = init_shell(display, buffer_utils, log.clone()); let dnd_icon = Arc::new(Mutex::new(None)); diff --git a/src/backend/egl/mod.rs b/src/backend/egl/mod.rs index 316df5d..6ecdde3 100644 --- a/src/backend/egl/mod.rs +++ b/src/backend/egl/mod.rs @@ -472,6 +472,42 @@ impl EGLDisplay { Err(BufferAccessError::ContextLost) } } + + /// Try to receive the dimensions of a given [`WlBuffer`]. + /// + /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm)) or the + /// context has been lost, `None` is returned. + pub fn egl_buffer_dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> { + if let Some(display) = self.egl.upgrade() { + let mut width: i32 = 0; + if unsafe { + ffi::egl::QueryWaylandBufferWL( + *display, + buffer.as_ref().c_ptr() as *mut _, + ffi::egl::WIDTH as i32, + &mut width as *mut _, + ) == 0 + } { + return None; + } + + let mut height: i32 = 0; + if unsafe { + ffi::egl::QueryWaylandBufferWL( + *display, + buffer.as_ref().c_ptr() as *mut _, + ffi::egl::HEIGHT as i32, + &mut height as *mut _, + ) == 0 + } { + return None; + } + + Some((width, height)) + } else { + None + } + } } #[cfg(feature = "native_lib")]