use std::collections::BTreeMap;
use std::io::Error as IoError;
use std::time::Duration;
use anyhow::anyhow;
use anyhow::Context;
use base::AsRawDescriptor;
use base::Error as BaseError;
use base::EventToken;
use base::EventType;
use base::VolatileSlice;
use base::WaitContext;
use remain::sorted;
use serde::Deserialize;
use serde::Serialize;
use sync::Waitable;
use thiserror::Error;
use vm_control::gpu::DisplayParameters;
use vm_control::gpu::MouseMode;
#[cfg(feature = "vulkan_display")]
use vulkano::VulkanLibrary;
mod event_device;
#[cfg(feature = "android_display")]
mod gpu_display_android;
#[cfg(feature = "android_display_stub")]
mod gpu_display_android_stub;
mod gpu_display_stub;
#[cfg(windows)]
mod gpu_display_win;
#[cfg(any(target_os = "android", target_os = "linux"))]
mod gpu_display_wl;
#[cfg(feature = "x")]
mod gpu_display_x;
#[cfg(any(windows, feature = "x"))]
mod keycode_converter;
mod sys;
#[cfg(feature = "vulkan_display")]
pub mod vulkan;
pub use event_device::EventDevice;
pub use event_device::EventDeviceKind;
#[cfg(windows)]
pub use gpu_display_win::WindowProcedureThread;
#[cfg(windows)]
pub use gpu_display_win::WindowProcedureThreadBuilder;
use linux_input_sys::virtio_input_event;
use sys::SysDisplayT;
pub use sys::SysGpuDisplayExt;
#[cfg(feature = "vulkan_display")]
const VK_UUID_BYTES: usize = 16;
#[derive(Clone)]
pub struct VulkanCreateParams {
#[cfg(feature = "vulkan_display")]
pub vulkan_library: std::sync::Arc<VulkanLibrary>,
#[cfg(feature = "vulkan_display")]
pub device_uuid: [u8; VK_UUID_BYTES],
#[cfg(feature = "vulkan_display")]
pub driver_uuid: [u8; VK_UUID_BYTES],
}
#[sorted]
#[derive(Error, Debug)]
pub enum GpuDisplayError {
#[error("internal allocation failed")]
Allocate,
#[error("received a base error: {0}")]
BaseError(BaseError),
#[error("failed to connect to compositor")]
Connect,
#[error("connection to compositor has been broken")]
ConnectionBroken,
#[error("failed to create event file descriptor")]
CreateEvent,
#[error("failed to crate surface on the compositor")]
CreateSurface,
#[error("failed to import an event device: {0}")]
FailedEventDeviceImport(String),
#[error("failed to register an event device to listen for guest events: {0}")]
FailedEventDeviceListen(base::TubeError),
#[error("failed to import a buffer to the compositor")]
FailedImport,
#[error("invalid Android display service name: {0}")]
InvalidAndroidDisplayServiceName(String),
#[error("invalid import ID")]
InvalidImportId,
#[error("invalid path")]
InvalidPath,
#[error("invalid surface ID")]
InvalidSurfaceId,
#[error("an input/output error occur: {0}")]
IoError(IoError),
#[error("required feature was missing: {0}")]
RequiredFeature(&'static str),
#[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)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum SurfaceType {
Scanout,
Cursor,
}
#[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
}
}
trait GpuDisplaySurface {
fn surface_descriptor(&self) -> u64 {
0
}
fn framebuffer(&mut self) -> Option<GpuDisplayFramebuffer> {
None
}
fn next_buffer_in_use(&self) -> bool {
false
}
fn close_requested(&self) -> bool {
false
}
fn flip(&mut self) {
}
fn flip_to(
&mut self,
_import_id: u32,
_acquire_timepoint: Option<SemaphoreTimepoint>,
_release_timepoint: Option<SemaphoreTimepoint>,
_extra_info: Option<FlipToExtraInfo>,
) -> anyhow::Result<Waitable> {
Ok(Waitable::signaled())
}
fn commit(&mut self) -> GpuDisplayResult<()> {
Ok(())
}
fn set_mouse_mode(&mut self, _mouse_mode: MouseMode) {
}
fn set_position(&mut self, _x: u32, _y: u32) {
}
#[allow(dead_code)]
fn buffer_completion_type(&self) -> u32 {
0
}
#[allow(dead_code)]
fn draw_current_buffer(&mut self) {
}
#[allow(dead_code)]
fn on_client_message(&mut self, _client_data: u64) {
}
#[allow(dead_code)]
fn on_shm_completion(&mut self, _shm_complete: u64) {
}
}
struct GpuDisplayEvents {
events: Vec<virtio_input_event>,
device_type: EventDeviceKind,
}
trait DisplayT: AsRawDescriptor {
fn pending_events(&self) -> bool {
false
}
fn flush(&self) {
}
fn next_event(&mut self) -> GpuDisplayResult<u64> {
Ok(0)
}
fn handle_next_event(
&mut self,
_surface: &mut Box<dyn GpuDisplaySurface>,
) -> Option<GpuDisplayEvents> {
None
}
fn create_surface(
&mut self,
parent_surface_id: Option<u32>,
surface_id: u32,
scanout_id: Option<u32>,
display_params: &DisplayParameters,
surf_type: SurfaceType,
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>>;
fn import_resource(
&mut self,
_import_id: u32,
_surface_id: u32,
_external_display_resource: DisplayExternalResourceImport,
) -> anyhow::Result<()> {
Err(anyhow!("import_resource is unsupported"))
}
fn release_import(&mut self, _import_id: u32, _surface_id: u32) {}
}
pub trait GpuDisplayExt {
fn import_event_device(&mut self, event_device: EventDevice) -> GpuDisplayResult<u32>;
fn handle_event_device(&mut self, event_device_id: u32);
}
pub enum DisplayExternalResourceImport<'a> {
Dmabuf {
descriptor: &'a dyn AsRawDescriptor,
offset: u32,
stride: u32,
modifiers: u64,
width: u32,
height: u32,
fourcc: u32,
},
VulkanImage {
descriptor: &'a dyn AsRawDescriptor,
metadata: VulkanDisplayImageImportMetadata,
},
VulkanTimelineSemaphore {
descriptor: &'a dyn AsRawDescriptor,
},
}
pub struct VkExtent3D {
pub width: u32,
pub height: u32,
pub depth: u32,
}
pub struct VulkanDisplayImageImportMetadata {
pub flags: u32,
pub image_type: i32,
pub format: i32,
pub extent: VkExtent3D,
pub mip_levels: u32,
pub array_layers: u32,
pub samples: u32,
pub tiling: i32,
pub usage: u32,
pub sharing_mode: i32,
pub queue_family_indices: Vec<u32>,
pub initial_layout: i32,
pub allocation_size: u64,
pub memory_type_index: u32,
pub dedicated_allocation: bool,
}
pub struct SemaphoreTimepoint {
pub import_id: u32,
pub value: u64,
}
pub enum FlipToExtraInfo {
#[cfg(feature = "vulkan_display")]
Vulkan { old_layout: i32, new_layout: i32 },
}
pub struct GpuDisplay {
next_id: u32,
event_devices: BTreeMap<u32, EventDevice>,
surfaces: BTreeMap<u32, Box<dyn GpuDisplaySurface>>,
wait_ctx: WaitContext<DisplayEventToken>,
inner: Box<dyn SysDisplayT>,
}
impl GpuDisplay {
pub fn open_x(display_name: Option<&str>) -> GpuDisplayResult<GpuDisplay> {
let _ = display_name;
#[cfg(feature = "x")]
{
let display = gpu_display_x::DisplayX::open_display(display_name)?;
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(),
wait_ctx,
})
}
#[cfg(not(feature = "x"))]
Err(GpuDisplayError::Unsupported)
}
pub fn open_android(service_name: &str) -> GpuDisplayResult<GpuDisplay> {
let _ = service_name;
#[cfg(feature = "android_display")]
{
let display = gpu_display_android::DisplayAndroid::new(service_name)?;
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(),
wait_ctx,
})
}
#[cfg(not(feature = "android_display"))]
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(),
wait_ctx,
})
}
pub fn take_event_devices(&mut self) -> Vec<EventDevice> {
std::mem::take(&mut self.event_devices)
.into_values()
.collect()
}
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(())
}
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(())
}
pub fn create_surface(
&mut self,
parent_surface_id: Option<u32>,
scanout_id: Option<u32>,
display_params: &DisplayParameters,
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,
scanout_id,
display_params,
surf_type,
)?;
self.next_id += 1;
self.surfaces.insert(new_surface_id, new_surface);
Ok(new_surface_id)
}
pub fn release_surface(&mut self, surface_id: u32) {
self.surfaces.remove(&surface_id);
}
pub fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer> {
let surface = self.surfaces.get_mut(&surface_id)?;
surface.framebuffer()
}
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)
}
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)
}
pub fn flip(&mut self, surface_id: u32) {
if let Some(surface) = self.surfaces.get_mut(&surface_id) {
surface.flip()
}
}
pub fn close_requested(&self, surface_id: u32) -> bool {
self.surfaces
.get(&surface_id)
.map(|s| s.close_requested())
.unwrap_or(true)
}
pub fn import_resource(
&mut self,
surface_id: u32,
external_display_resource: DisplayExternalResourceImport,
) -> anyhow::Result<u32> {
let import_id = self.next_id;
self.inner
.import_resource(import_id, surface_id, external_display_resource)?;
self.next_id += 1;
Ok(import_id)
}
pub fn release_import(&mut self, import_id: u32, surface_id: u32) {
self.inner.release_import(import_id, surface_id);
}
pub fn commit(&mut self, surface_id: u32) -> GpuDisplayResult<()> {
let surface = self
.surfaces
.get_mut(&surface_id)
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
surface.commit()
}
pub fn flip_to(
&mut self,
surface_id: u32,
import_id: u32,
acquire_timepoint: Option<SemaphoreTimepoint>,
release_timepoint: Option<SemaphoreTimepoint>,
extra_info: Option<FlipToExtraInfo>,
) -> anyhow::Result<Waitable> {
let surface = self
.surfaces
.get_mut(&surface_id)
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
surface
.flip_to(import_id, acquire_timepoint, release_timepoint, extra_info)
.context("failed in flip on GpuDisplaySurface")
}
pub fn set_mouse_mode(
&mut self,
surface_id: u32,
mouse_mode: MouseMode,
) -> GpuDisplayResult<()> {
let surface = self
.surfaces
.get_mut(&surface_id)
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
surface.set_mouse_mode(mouse_mode);
Ok(())
}
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(())
}
}