| // 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 base::Error as SysError; |
| use data_model::VolatileSlice; |
| |
| mod event_device; |
| mod gpu_display_stub; |
| mod gpu_display_wl; |
| #[cfg(feature = "x")] |
| mod gpu_display_x; |
| mod keycode_converter; |
| |
| pub use event_device::{EventDevice, EventDeviceKind}; |
| |
| /// An error generated by `GpuDisplay`. |
| #[derive(Debug)] |
| pub enum GpuDisplayError { |
| /// An internal allocation failed. |
| Allocate, |
| /// Connecting to the compositor failed. |
| Connect, |
| /// Creating event file descriptor failed. |
| CreateEvent, |
| /// Creating shared memory failed. |
| CreateShm(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"), |
| CreateEvent => write!(f, "failed to create event file descriptor"), |
| 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), |
| 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<GpuDisplayFramebuffer<'a>> { |
| 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 usize, count as usize) |
| .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<u32, GpuDisplayError>; |
| fn release_import(&mut self, import_id: u32); |
| fn dispatch_events(&mut self); |
| fn create_surface( |
| &mut self, |
| parent_surface_id: Option<u32>, |
| width: u32, |
| height: u32, |
| ) -> Result<u32, GpuDisplayError>; |
| fn release_surface(&mut self, surface_id: u32); |
| fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer>; |
| fn framebuffer_region( |
| &mut self, |
| surface_id: u32, |
| x: u32, |
| y: u32, |
| width: u32, |
| height: u32, |
| ) -> Option<GpuDisplayFramebuffer> { |
| 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); |
| fn import_event_device(&mut self, event_device: EventDevice) -> Result<u32, GpuDisplayError>; |
| fn release_event_device(&mut self, event_device_id: u32); |
| fn attach_event_device(&mut self, surface_id: u32, event_device_id: 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<dyn DisplayT>, |
| is_x: bool, |
| } |
| |
| impl GpuDisplay { |
| pub fn open_x<S: AsRef<str>>(display_name: Option<S>) -> Result<GpuDisplay, GpuDisplayError> { |
| 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, is_x: true }) |
| } |
| #[cfg(not(feature = "x"))] |
| Err(GpuDisplayError::Unsupported) |
| } |
| |
| /// Opens a fresh connection to the compositor. |
| pub fn open_wayland<P: AsRef<Path>>( |
| wayland_path: Option<P>, |
| ) -> Result<GpuDisplay, GpuDisplayError> { |
| 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, is_x: false }) |
| } |
| |
| pub fn open_stub() -> Result<GpuDisplay, GpuDisplayError> { |
| let display = gpu_display_stub::DisplayStub::new()?; |
| let inner = Box::new(display); |
| Ok(GpuDisplay { inner, is_x: false }) |
| } |
| |
| /// Return whether this display is an X display |
| pub fn is_x(&self) -> bool { |
| self.is_x |
| } |
| |
| /// 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<u32, GpuDisplayError> { |
| 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<u32>, |
| width: u32, |
| height: u32, |
| ) -> Result<u32, GpuDisplayError> { |
| 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<GpuDisplayFramebuffer> { |
| 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<GpuDisplayFramebuffer> { |
| 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) |
| } |
| |
| pub fn import_event_device( |
| &mut self, |
| event_device: EventDevice, |
| ) -> Result<u32, GpuDisplayError> { |
| self.inner.import_event_device(event_device) |
| } |
| |
| pub fn release_event_device(&mut self, event_device_id: u32) { |
| self.inner.release_event_device(event_device_id) |
| } |
| |
| pub fn attach_event_device(&mut self, surface_id: u32, event_device_id: u32) { |
| self.inner.attach_event_device(surface_id, event_device_id); |
| } |
| } |
| |
| impl AsRawFd for GpuDisplay { |
| fn as_raw_fd(&self) -> RawFd { |
| self.inner.as_raw_fd() |
| } |
| } |