// Copyright 2018 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! Crate for displaying simple surfaces and GPU buffers over wayland. use std::fmt::{self, Display}; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; use data_model::VolatileSlice; use sys_util::Error as SysError; mod gpu_display_wl; #[cfg(feature = "x")] mod gpu_display_x; /// An error generated by `GpuDisplay`. #[derive(Debug)] pub enum GpuDisplayError { /// An internal allocation failed. Allocate, /// Connecting to the compositor failed. Connect, /// Creating shared memory failed. CreateShm(SysError), /// Setting the size of shared memory failed. SetSize(SysError), /// Failed to create a surface on the compositor. CreateSurface, /// Failed to import a buffer to the compositor. FailedImport, /// The surface ID is invalid. InvalidSurfaceId, /// A required feature was missing. RequiredFeature(&'static str), /// The path is invalid. InvalidPath, /// The method is unsupported by the implementation. Unsupported, } impl Display for GpuDisplayError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::GpuDisplayError::*; match self { Allocate => write!(f, "internal allocation failed"), Connect => write!(f, "failed to connect to compositor"), CreateShm(e) => write!(f, "failed to create shared memory: {}", e), CreateSurface => write!(f, "failed to crate surface on the compositor"), FailedImport => write!(f, "failed to import a buffer to the compositor"), InvalidPath => write!(f, "invalid path"), InvalidSurfaceId => write!(f, "invalid surface ID"), RequiredFeature(feature) => write!(f, "required feature was missing: {}", feature), SetSize(e) => write!(f, "failed to set size of shared memory: {}", e), Unsupported => write!(f, "unsupported by the implementation"), } } } #[derive(Clone)] pub struct GpuDisplayFramebuffer<'a> { framebuffer: VolatileSlice<'a>, slice: VolatileSlice<'a>, stride: u32, bytes_per_pixel: u32, } impl<'a> GpuDisplayFramebuffer<'a> { fn new( framebuffer: VolatileSlice<'a>, stride: u32, bytes_per_pixel: u32, ) -> GpuDisplayFramebuffer { GpuDisplayFramebuffer { framebuffer, slice: framebuffer, stride, bytes_per_pixel, } } fn sub_region( &self, x: u32, y: u32, width: u32, height: u32, ) -> Option> { let x_byte_offset = x.checked_mul(self.bytes_per_pixel)?; let y_byte_offset = y.checked_mul(self.stride)?; let byte_offset = x_byte_offset.checked_add(y_byte_offset)?; let width_bytes = width.checked_mul(self.bytes_per_pixel)?; let count = height .checked_mul(self.stride)? .checked_sub(self.stride)? .checked_add(width_bytes)?; let slice = self .framebuffer .sub_slice(byte_offset as u64, count as u64) .unwrap(); Some(GpuDisplayFramebuffer { slice, ..*self }) } pub fn as_volatile_slice(&self) -> VolatileSlice<'a> { self.slice } pub fn stride(&self) -> u32 { self.stride } } trait DisplayT: AsRawFd { fn import_dmabuf( &mut self, fd: RawFd, offset: u32, stride: u32, modifiers: u64, width: u32, height: u32, fourcc: u32, ) -> Result; fn release_import(&mut self, import_id: u32); fn dispatch_events(&mut self); fn create_surface( &mut self, parent_surface_id: Option, width: u32, height: u32, ) -> Result; fn release_surface(&mut self, surface_id: u32); fn framebuffer(&mut self, surface_id: u32) -> Option; fn framebuffer_region( &mut self, surface_id: u32, x: u32, y: u32, width: u32, height: u32, ) -> Option { let framebuffer = self.framebuffer(surface_id)?; framebuffer.sub_region(x, y, width, height) } fn commit(&mut self, surface_id: u32); fn next_buffer_in_use(&self, surface_id: u32) -> bool; fn flip(&mut self, surface_id: u32); fn flip_to(&mut self, surface_id: u32, import_id: u32); fn close_requested(&self, surface_id: u32) -> bool; fn set_position(&mut self, surface_id: u32, x: u32, y: u32); } /// A connection to the compositor and associated collection of state. /// /// The user of `GpuDisplay` can use `AsRawFd` to poll on the compositor connection's file /// descriptor. When the connection is readable, `dispatch_events` can be called to process it. pub struct GpuDisplay { inner: Box, } impl GpuDisplay { pub fn open_x>(display_name: Option) -> Result { let _ = display_name; #[cfg(feature = "x")] { let display = match display_name { Some(s) => gpu_display_x::DisplayX::open_display(Some(s.as_ref()))?, None => gpu_display_x::DisplayX::open_display(None)?, }; let inner = Box::new(display); Ok(GpuDisplay { inner }) } #[cfg(not(feature = "x"))] Err(GpuDisplayError::Unsupported) } /// Opens a fresh connection to the compositor. pub fn open_wayland>( wayland_path: Option

, ) -> Result { let display = match wayland_path { Some(s) => gpu_display_wl::DisplayWl::new(Some(s.as_ref()))?, None => gpu_display_wl::DisplayWl::new(None)?, }; let inner = Box::new(display); Ok(GpuDisplay { inner }) } /// Imports a dmabuf to the compositor for use as a surface buffer and returns a handle to it. pub fn import_dmabuf( &mut self, fd: RawFd, offset: u32, stride: u32, modifiers: u64, width: u32, height: u32, fourcc: u32, ) -> Result { self.inner .import_dmabuf(fd, offset, stride, modifiers, width, height, fourcc) } /// Releases a previously imported dmabuf identified by the given handle. pub fn release_import(&mut self, import_id: u32) { self.inner.release_import(import_id); } /// Dispatches internal events that were received from the compositor since the last call to /// `dispatch_events`. pub fn dispatch_events(&mut self) { self.inner.dispatch_events() } /// Creates a surface on the the compositor as either a top level window, or child of another /// surface, returning a handle to the new surface. pub fn create_surface( &mut self, parent_surface_id: Option, width: u32, height: u32, ) -> Result { self.inner.create_surface(parent_surface_id, width, height) } /// Releases a previously created surface identified by the given handle. pub fn release_surface(&mut self, surface_id: u32) { self.inner.release_surface(surface_id) } /// Gets a reference to an unused framebuffer for the identified surface. pub fn framebuffer(&mut self, surface_id: u32) -> Option { self.inner.framebuffer(surface_id) } /// Gets a reference to an unused framebuffer for the identified surface. pub fn framebuffer_region( &mut self, surface_id: u32, x: u32, y: u32, width: u32, height: u32, ) -> Option { self.inner .framebuffer_region(surface_id, x, y, width, height) } /// Commits any pending state for the identified surface. pub fn commit(&mut self, surface_id: u32) { self.inner.commit(surface_id) } /// Returns true if the next buffer in the buffer queue for the given surface is currently in /// use. /// /// If the next buffer is in use, the memory returned from `framebuffer_memory` should not be /// written to. pub fn next_buffer_in_use(&self, surface_id: u32) -> bool { self.inner.next_buffer_in_use(surface_id) } /// Changes the visible contents of the identified surface to the contents of the framebuffer /// last returned by `framebuffer_memory` for this surface. pub fn flip(&mut self, surface_id: u32) { self.inner.flip(surface_id) } /// Changes the visible contents of the identified surface to that of the identified imported /// buffer. pub fn flip_to(&mut self, surface_id: u32, import_id: u32) { self.inner.flip_to(surface_id, import_id) } /// Returns true if the identified top level surface has been told to close by the compositor, /// and by extension the user. pub fn close_requested(&self, surface_id: u32) -> bool { self.inner.close_requested(surface_id) } /// Sets the position of the identified subsurface relative to its parent. /// /// The change in position will not be visible until `commit` is called for the parent surface. pub fn set_position(&mut self, surface_id: u32, x: u32, y: u32) { self.inner.set_position(surface_id, x, y) } } impl AsRawFd for GpuDisplay { fn as_raw_fd(&self) -> RawFd { self.inner.as_raw_fd() } }