blob: ef7605b6ced8a791abd6f2b38d39ab8dff6f7736 [file] [log] [blame]
// Copyright 2018 The ChromiumOS Authors
// 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 a low-level display backend such as
//! Wayland or X.
use std::collections::BTreeMap;
use std::io::Error as IoError;
use std::time::Duration;
use base::AsRawDescriptor;
use base::Error as BaseError;
use base::EventToken;
use base::EventType;
use base::WaitContext;
use data_model::VolatileSlice;
use remain::sorted;
use thiserror::Error;
mod event_device;
mod gpu_display_stub;
#[cfg(windows)]
mod gpu_display_win;
#[cfg(unix)]
mod gpu_display_wl;
#[cfg(feature = "x")]
mod gpu_display_x;
#[cfg(feature = "x")]
mod keycode_converter;
mod sys;
pub use event_device::EventDevice;
pub use event_device::EventDeviceKind;
#[cfg(windows)]
pub use gpu_display_win::DisplayProperties as WinDisplayProperties;
use linux_input_sys::virtio_input_event;
use sys::SysDisplayT;
pub use sys::SysGpuDisplayExt;
#[cfg(windows)]
pub type WindowProcedureThread = gpu_display_win::WindowProcedureThread<gpu_display_win::Surface>;
/// An error generated by `GpuDisplay`.
#[sorted]
#[derive(Error, Debug)]
pub enum GpuDisplayError {
/// An internal allocation failed.
#[error("internal allocation failed")]
Allocate,
/// A base error occurred.
#[error("received a base error: {0}")]
BaseError(BaseError),
/// Connecting to the compositor failed.
#[error("failed to connect to compositor")]
Connect,
/// Connection to compositor has been broken.
#[error("connection to compositor has been broken")]
ConnectionBroken,
/// Creating event file descriptor failed.
#[error("failed to create event file descriptor")]
CreateEvent,
/// Failed to create a surface on the compositor.
#[error("failed to crate surface on the compositor")]
CreateSurface,
/// Failed to import an event device.
#[error("failed to import an event device: {0}")]
FailedEventDeviceImport(String),
/// Failed to import a buffer to the compositor.
#[error("failed to import a buffer to the compositor")]
FailedImport,
/// The import ID is invalid.
#[error("invalid import ID")]
InvalidImportId,
/// The path is invalid.
#[error("invalid path")]
InvalidPath,
/// The surface ID is invalid.
#[error("invalid surface ID")]
InvalidSurfaceId,
/// An input/output error occured.
#[error("an input/output error occur: {0}")]
IoError(IoError),
/// A required feature was missing.
#[error("required feature was missing: {0}")]
RequiredFeature(&'static str),
/// The method is unsupported by the implementation.
#[error("unsupported by the implementation")]
Unsupported,
}
pub type GpuDisplayResult<T> = std::result::Result<T, GpuDisplayError>;
impl From<BaseError> for GpuDisplayError {
fn from(e: BaseError) -> GpuDisplayError {
GpuDisplayError::BaseError(e)
}
}
impl From<IoError> for GpuDisplayError {
fn from(e: IoError) -> GpuDisplayError {
GpuDisplayError::IoError(e)
}
}
/// A surface type
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SurfaceType {
/// Scanout surface
Scanout,
/// Mouse cursor surface
Cursor,
}
/// Event token for display instances
#[derive(EventToken, Debug)]
pub enum DisplayEventToken {
Display,
EventDevice { event_device_id: u32 },
}
#[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
}
}
/// Empty trait, just used as a bounds for now
trait GpuDisplayImport {}
trait GpuDisplaySurface {
/// Returns an unique ID associated with the surface. This is typically generated by the
/// compositor or cast of a raw pointer.
fn surface_descriptor(&self) -> u64 {
0
}
/// Returns the next framebuffer, allocating if necessary.
fn framebuffer(&mut self) -> Option<GpuDisplayFramebuffer> {
None
}
/// Returns true if the next buffer in the swapchain is already in use.
fn next_buffer_in_use(&self) -> bool {
false
}
/// Returns true if the surface should be closed.
fn close_requested(&self) -> bool {
false
}
/// Puts the next buffer on the screen, making it the current buffer.
fn flip(&mut self) {
// no-op
}
/// Puts the specified import_id on the screen.
fn flip_to(&mut self, _import_id: u32) {
// no-op
}
/// Commits the surface to the compositor.
fn commit(&mut self) -> GpuDisplayResult<()> {
Ok(())
}
/// Sets the position of the identified subsurface relative to its parent.
fn set_position(&mut self, _x: u32, _y: u32) {
// no-op
}
/// Returns the type of the completed buffer.
fn buffer_completion_type(&self) -> u32 {
0
}
/// Draws the current buffer on the screen.
fn draw_current_buffer(&mut self) {
// no-op
}
/// Handles a compositor-specific client event.
fn on_client_message(&mut self, _client_data: u64) {
// no-op
}
/// Handles a compositor-specific shared memory completion event.
fn on_shm_completion(&mut self, _shm_complete: u64) {
// no-op
}
/// Sets the scanout ID for the surface.
fn set_scanout_id(&mut self, _scanout_id: u32) {
// no-op
}
}
struct GpuDisplayEvents {
events: Vec<virtio_input_event>,
device_type: EventDeviceKind,
}
trait DisplayT: AsRawDescriptor {
/// Returns true if there are events that are on the queue.
fn pending_events(&self) -> bool {
false
}
/// Sends any pending commands to the compositor.
fn flush(&self) {
// no-op
}
/// Returns the surface descirptor associated with the current event
fn next_event(&mut self) -> GpuDisplayResult<u64> {
Ok(0)
}
/// Handles the event from the compositor, and returns an list of events
fn handle_next_event(
&mut self,
_surface: &mut Box<dyn GpuDisplaySurface>,
) -> Option<GpuDisplayEvents> {
None
}
/// Creates a surface with the given parameters. The display backend is given a non-zero
/// `surface_id` as a handle for subsequent operations.
fn create_surface(
&mut self,
parent_surface_id: Option<u32>,
surface_id: u32,
width: u32,
height: u32,
surf_type: SurfaceType,
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>>;
/// Imports memory into the display backend. The display backend is given a non-zero
/// `import_id` as a handle for subsequent operations.
fn import_memory(
&mut self,
_import_id: u32,
_descriptor: &dyn AsRawDescriptor,
_offset: u32,
_stride: u32,
_modifiers: u64,
_width: u32,
_height: u32,
_fourcc: u32,
) -> GpuDisplayResult<Box<dyn GpuDisplayImport>> {
Err(GpuDisplayError::Unsupported)
}
}
pub trait GpuDisplayExt {
/// Imports the given `event_device` into the display, returning an event device id on success.
/// This device may be used to poll for input events.
fn import_event_device(&mut self, event_device: EventDevice) -> GpuDisplayResult<u32>;
}
/// A connection to the compositor and associated collection of state.
///
/// The user of `GpuDisplay` can use `AsRawDescriptor` 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 {
next_id: u32,
event_devices: BTreeMap<u32, EventDevice>,
surfaces: BTreeMap<u32, Box<dyn GpuDisplaySurface>>,
imports: BTreeMap<u32, Box<dyn GpuDisplayImport>>,
// `inner` must be after `imports` and `surfaces` to ensure those objects are dropped before
// the display context. The drop order for fields inside a struct is the order in which they
// are declared [Rust RFC 1857].
inner: Box<dyn SysDisplayT>,
wait_ctx: WaitContext<DisplayEventToken>,
is_x: bool,
}
impl GpuDisplay {
/// Opens a connection to X server
pub fn open_x<S: AsRef<str>>(display_name: Option<S>) -> GpuDisplayResult<GpuDisplay> {
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 wait_ctx = WaitContext::new()?;
wait_ctx.add(&display, DisplayEventToken::Display)?;
Ok(GpuDisplay {
inner: Box::new(display),
next_id: 1,
event_devices: Default::default(),
surfaces: Default::default(),
imports: Default::default(),
wait_ctx,
is_x: true,
})
}
#[cfg(not(feature = "x"))]
Err(GpuDisplayError::Unsupported)
}
pub fn open_stub() -> GpuDisplayResult<GpuDisplay> {
let display = gpu_display_stub::DisplayStub::new()?;
let wait_ctx = WaitContext::new()?;
wait_ctx.add(&display, DisplayEventToken::Display)?;
Ok(GpuDisplay {
inner: Box::new(display),
next_id: 1,
event_devices: Default::default(),
surfaces: Default::default(),
imports: Default::default(),
wait_ctx,
is_x: false,
})
}
/// Return whether this display is an X display
pub fn is_x(&self) -> bool {
self.is_x
}
fn handle_event_device(&mut self, event_device_id: u32) {
if let Some(event_device) = self.event_devices.get(&event_device_id) {
// TODO(zachr): decode the event and forward to the device.
let _ = event_device.recv_event_encoded();
}
}
fn dispatch_display_events(&mut self) -> GpuDisplayResult<()> {
self.inner.flush();
while self.inner.pending_events() {
let surface_descriptor = self.inner.next_event()?;
for surface in self.surfaces.values_mut() {
if surface_descriptor != surface.surface_descriptor() {
continue;
}
if let Some(gpu_display_events) = self.inner.handle_next_event(surface) {
for event_device in self.event_devices.values_mut() {
if event_device.kind() != gpu_display_events.device_type {
continue;
}
event_device.send_report(gpu_display_events.events.iter().cloned())?;
}
}
}
}
Ok(())
}
/// Dispatches internal events that were received from the compositor since the last call to
/// `dispatch_events`.
pub fn dispatch_events(&mut self) -> GpuDisplayResult<()> {
let wait_events = self.wait_ctx.wait_timeout(Duration::default())?;
if let Some(wait_event) = wait_events.iter().find(|e| e.is_hungup) {
base::error!(
"Display signaled with a hungup event for token {:?}",
wait_event.token
);
self.wait_ctx = WaitContext::new().unwrap();
return GpuDisplayResult::Err(GpuDisplayError::ConnectionBroken);
}
for wait_event in wait_events.iter().filter(|e| e.is_writable) {
if let DisplayEventToken::EventDevice { event_device_id } = wait_event.token {
if let Some(event_device) = self.event_devices.get_mut(&event_device_id) {
if !event_device.flush_buffered_events()? {
continue;
}
self.wait_ctx.modify(
event_device,
EventType::Read,
DisplayEventToken::EventDevice { event_device_id },
)?;
}
}
}
for wait_event in wait_events.iter().filter(|e| e.is_readable) {
match wait_event.token {
DisplayEventToken::Display => self.dispatch_display_events()?,
DisplayEventToken::EventDevice { event_device_id } => {
self.handle_event_device(event_device_id)
}
}
}
Ok(())
}
/// 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,
surf_type: SurfaceType,
) -> GpuDisplayResult<u32> {
if let Some(parent_id) = parent_surface_id {
if !self.surfaces.contains_key(&parent_id) {
return Err(GpuDisplayError::InvalidSurfaceId);
}
}
let new_surface_id = self.next_id;
let new_surface = self.inner.create_surface(
parent_surface_id,
new_surface_id,
width,
height,
surf_type,
)?;
self.next_id += 1;
self.surfaces.insert(new_surface_id, new_surface);
Ok(new_surface_id)
}
/// Releases a previously created surface identified by the given handle.
pub fn release_surface(&mut self, surface_id: u32) {
self.surfaces.remove(&surface_id);
}
/// Gets a reference to an unused framebuffer for the identified surface.
pub fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer> {
let surface = self.surfaces.get_mut(&surface_id)?;
surface.framebuffer()
}
/// 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> {
let framebuffer = self.framebuffer(surface_id)?;
framebuffer.sub_region(x, y, width, height)
}
/// 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.surfaces
.get(&surface_id)
.map(|s| s.next_buffer_in_use())
.unwrap_or(false)
}
/// 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) {
if let Some(surface) = self.surfaces.get_mut(&surface_id) {
surface.flip()
}
}
/// 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.surfaces
.get(&surface_id)
.map(|s| s.close_requested())
.unwrap_or(true)
}
/// Imports memory to the compositor for use as a surface buffer and returns a handle
/// to it.
pub fn import_memory(
&mut self,
descriptor: &dyn AsRawDescriptor,
offset: u32,
stride: u32,
modifiers: u64,
width: u32,
height: u32,
fourcc: u32,
) -> GpuDisplayResult<u32> {
let import_id = self.next_id;
let gpu_display_memory = self.inner.import_memory(
import_id, descriptor, offset, stride, modifiers, width, height, fourcc,
)?;
self.next_id += 1;
self.imports.insert(import_id, gpu_display_memory);
Ok(import_id)
}
/// Releases a previously imported memory identified by the given handle.
pub fn release_import(&mut self, import_id: u32) {
self.imports.remove(&import_id);
}
/// Commits any pending state for the identified surface.
pub fn commit(&mut self, surface_id: u32) -> GpuDisplayResult<()> {
let surface = self
.surfaces
.get_mut(&surface_id)
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
surface.commit()
}
/// 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) -> GpuDisplayResult<()> {
let surface = self
.surfaces
.get_mut(&surface_id)
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
if !self.imports.contains_key(&import_id) {
return Err(GpuDisplayError::InvalidImportId);
}
surface.flip_to(import_id);
Ok(())
}
/// 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) -> GpuDisplayResult<()> {
let surface = self
.surfaces
.get_mut(&surface_id)
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
surface.set_position(x, y);
Ok(())
}
/// Associates the scanout id with the given surface.
pub fn set_scanout_id(&mut self, surface_id: u32, scanout_id: u32) -> GpuDisplayResult<()> {
let surface = self
.surfaces
.get_mut(&surface_id)
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
surface.set_scanout_id(scanout_id);
Ok(())
}
}