From 46348367b1f8d14443c3c1d7bc1bcb39f631b9b1 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Mon, 3 Feb 2020 08:39:28 +0300 Subject: [PATCH 1/3] backend.egl: add egl_buffer_dimensions Allows to retrieve just the dimensions, without creating images and whatnot. --- src/backend/egl/mod.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) 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")] From 825995687ae47ed8f68fdb952d397ecb61ace822 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Mon, 3 Feb 2020 08:40:25 +0300 Subject: [PATCH 2/3] anvil: add BufferUtils A helper struct which currently contains only a method for retrieving buffer dimensions. Will be used for getting the buffer dimensions early, before rendering, to always have up-to-date size which is not zero. --- anvil/src/buffer_utils.rs | 59 +++++++++++++++++++++++++++++++++++++++ anvil/src/main.rs | 1 + 2 files changed, 60 insertions(+) create mode 100644 anvil/src/buffer_utils.rs 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; From a224f774eecae2f84e01c406366f46a86966637e Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Mon, 3 Feb 2020 08:42:12 +0300 Subject: [PATCH 3/3] anvil: store buffer dimensions separately Before this change, the texture size was used for the dimensions. However, the texture is not created until the next rendered frame, which means that frequently size was returned as zero, resulting in pointer focus artifacts. With this change, the dimensions are retrieved immediately on surface commit. --- anvil/src/shell.rs | 28 +++++++++++++++++++--------- anvil/src/udev.rs | 9 ++++++++- anvil/src/winit.rs | 10 ++++++++-- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 21ddb8e..5c40a62 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -28,7 +28,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 ] @@ -47,6 +50,7 @@ pub type MyCompositorToken = CompositorToken; pub fn init_shell( display: &mut Display, + buffer_utils: BufferUtils, log: ::slog::Logger, ) -> ( CompositorToken, @@ -58,7 +62,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), @@ -138,10 +142,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(); @@ -157,6 +166,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 @@ -164,6 +175,7 @@ fn surface_commit(surface: &wl_surface::WlSurface, token: CompositorToken old_buffer.release(); } data.texture = None; + data.dimensions = None; } None => {} } @@ -171,12 +183,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));