mod keyboard_input_manager;
mod math_util;
mod mouse_input_manager;
pub mod surface;
mod virtual_display_manager;
mod window;
mod window_manager;
mod window_message_dispatcher;
mod window_message_processor;
pub mod window_procedure_thread;
use std::collections::HashMap;
use std::num::NonZeroU32;
use std::rc::Rc;
use std::sync::mpsc::channel;
use std::sync::Arc;
use std::sync::Weak;
use std::time::Duration;
use anyhow::bail;
use anyhow::format_err;
#[cfg(feature = "vulkan_display")]
use anyhow::Context;
use anyhow::Result;
use base::error;
use base::info;
use base::warn;
use base::AsRawDescriptor;
use base::Descriptor;
use base::Event;
use base::EventWaitResult;
use base::RawDescriptor;
use base::ReadNotifier;
use base::SendTube;
use metrics::sys::windows::Metrics;
pub use surface::Surface;
use sync::Mutex;
use sync::Waitable;
use vm_control::gpu::DisplayParameters;
use vm_control::ModifyWaitContext;
use window_message_processor::DisplaySendToWndProc;
pub use window_procedure_thread::WindowProcedureThread;
pub use window_procedure_thread::WindowProcedureThreadBuilder;
#[cfg(feature = "vulkan_display")]
use crate::gpu_display_win::window::BasicWindow;
#[cfg(feature = "vulkan_display")]
use crate::vulkan::HostDisplay;
use crate::DisplayExternalResourceImport;
use crate::DisplayT;
use crate::EventDevice;
use crate::FlipToExtraInfo;
use crate::GpuDisplayError;
use crate::GpuDisplayResult;
use crate::GpuDisplaySurface;
use crate::MouseMode;
use crate::SemaphoreTimepoint;
use crate::SurfaceType;
use crate::SysDisplayT;
use crate::VulkanCreateParams;
pub(crate) type ObjectId = NonZeroU32;
pub struct VirtualDisplaySpace;
pub struct HostWindowSpace;
pub enum HostDisplayWrapper {
Uninitialized,
#[cfg(feature = "vulkan_display")]
Initialized(Box<HostDisplay>),
}
pub struct DisplayWin {
wndproc_thread: Rc<WindowProcedureThread>,
close_requested_event: Event,
win_metrics: Option<Weak<Metrics>>,
is_surface_created: bool,
#[allow(dead_code)]
gpu_display_wait_descriptor_ctrl: SendTube,
event_device_wait_descriptor_requests: Vec<ModifyWaitContext>,
host_displays: HashMap<u32, Arc<Mutex<HostDisplayWrapper>>>,
#[allow(dead_code)]
vulkan_display_create_params: Option<VulkanCreateParams>,
}
impl DisplayWin {
pub fn new(
wndproc_thread: WindowProcedureThread,
win_metrics: Option<Weak<Metrics>>,
gpu_display_wait_descriptor_ctrl: SendTube,
vulkan_display_create_params: Option<VulkanCreateParams>,
) -> Result<DisplayWin, GpuDisplayError> {
let close_requested_event =
wndproc_thread
.try_clone_close_requested_event()
.map_err(|e| {
error!("Failed to create DisplayWin: {:?}", e);
GpuDisplayError::Allocate
})?;
Ok(Self {
wndproc_thread: Rc::new(wndproc_thread),
close_requested_event,
win_metrics,
is_surface_created: false,
gpu_display_wait_descriptor_ctrl,
event_device_wait_descriptor_requests: Vec::new(),
host_displays: HashMap::new(),
vulkan_display_create_params,
})
}
fn create_surface_internal(
&mut self,
surface_id: u32,
scanout_id: u32,
display_params: &DisplayParameters,
) -> Result<Arc<Mutex<HostDisplayWrapper>>> {
let display_params_clone = display_params.clone();
let metrics = self.win_metrics.clone();
#[cfg(feature = "vulkan_display")]
let vulkan_create_params = self.vulkan_display_create_params.clone();
let (result_sender, result_receiver) = channel();
#[allow(unused_variables)]
let (host_display_sender, host_display_receiver) = channel();
self.wndproc_thread
.post_display_command(DisplaySendToWndProc::CreateSurface {
scanout_id,
function: Box::new(move |window, display_event_dispatcher| {
#[cfg(feature = "vulkan_display")]
let host_display = {
let create_display_closure =
|VulkanCreateParams {
vulkan_library,
device_uuid,
driver_uuid,
}| {
unsafe {
let initial_host_viewport_size = window
.get_client_rect()
.with_context(|| "retrieve window client area size")?
.size;
HostDisplay::new(
vulkan_library,
window.handle() as _,
&initial_host_viewport_size.cast_unit(),
device_uuid,
driver_uuid,
)
.with_context(|| "create vulkan display")
}
};
let host_display = vulkan_create_params
.map(create_display_closure)
.transpose()?;
let host_display = match host_display {
Some(host_display) => {
HostDisplayWrapper::Initialized(Box::new(host_display))
}
None => HostDisplayWrapper::Uninitialized,
};
let host_display = Arc::new(Mutex::new(host_display));
host_display_sender
.send(Arc::clone(&host_display))
.map_err(|_| {
format_err!("Failed to send vulkan display back to caller.")
})?;
host_display
};
#[cfg(not(feature = "vulkan_display"))]
let host_display = Arc::new(Mutex::new(HostDisplayWrapper::Uninitialized));
Surface::new(
surface_id,
window,
metrics,
&display_params_clone,
display_event_dispatcher,
host_display,
)
}),
callback: Box::new(move |success| {
if let Err(e) = result_sender.send(success) {
error!("Failed to send surface creation result: {}", e);
}
}),
})?;
match result_receiver.recv() {
Ok(true) => host_display_receiver.recv().map_err(|_| {
format_err!(
"Failed to receive the vulkan display from the surface creation routine."
)
}),
Ok(false) => bail!("WndProc thread failed to create surface!"),
Err(e) => bail!("Failed to receive surface creation result: {}", e),
}
}
fn import_event_device_internal(
&mut self,
event_device_id: u32,
event_device: EventDevice,
) -> Result<()> {
match ObjectId::new(event_device_id) {
Some(event_device_id) => {
let req = ModifyWaitContext::Add(Descriptor(
event_device.get_read_notifier().as_raw_descriptor(),
));
if let Err(e) = self.wndproc_thread.post_display_command(
DisplaySendToWndProc::ImportEventDevice {
event_device_id,
event_device,
},
) {
bail!("Failed to post ImportEventDevice message: {:?}", e);
}
if self.is_surface_created {
if let Err(e) = self.gpu_display_wait_descriptor_ctrl.send(&req) {
bail!(
"failed to send event device descriptor to \
GPU worker's wait context: {:?}",
e
)
}
} else {
self.event_device_wait_descriptor_requests.push(req);
}
Ok(())
}
None => bail!("{} cannot be converted to ObjectId", event_device_id),
}
}
}
impl AsRawDescriptor for DisplayWin {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.close_requested_event.as_raw_descriptor()
}
}
impl DisplayT for DisplayWin {
fn create_surface(
&mut self,
parent_surface_id: Option<u32>,
surface_id: u32,
scanout_id: Option<u32>,
display_params: &DisplayParameters,
surface_type: SurfaceType,
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
if parent_surface_id.is_some() {
return Err(GpuDisplayError::Unsupported);
}
if !matches!(surface_type, SurfaceType::Scanout) {
return Err(GpuDisplayError::Unsupported);
}
let host_display = match self.create_surface_internal(
surface_id,
scanout_id.expect("scanout id is required"),
display_params,
) {
Err(e) => {
error!("Failed to create surface: {:?}", e);
return Err(GpuDisplayError::Allocate);
}
Ok(display) => display,
};
self.is_surface_created = true;
self.host_displays
.insert(surface_id, Arc::clone(&host_display));
for req in self.event_device_wait_descriptor_requests.drain(..) {
if let Err(e) = self.gpu_display_wait_descriptor_ctrl.send(&req) {
error!(
"failed to send event device descriptor to GPU worker's wait context: {:?}",
e
);
return Err(GpuDisplayError::FailedEventDeviceListen(e));
}
}
Ok(Box::new(SurfaceWin {
surface_id,
wndproc_thread: Rc::downgrade(&self.wndproc_thread),
close_requested_event: self.close_requested_event.try_clone().map_err(|e| {
error!("Failed to clone close_requested_event: {}", e);
GpuDisplayError::Allocate
})?,
host_display,
}))
}
fn import_resource(
&mut self,
#[allow(unused_variables)] import_id: u32,
surface_id: u32,
#[allow(unused_variables)] external_display_resource: DisplayExternalResourceImport,
) -> Result<()> {
match self.host_displays.get(&surface_id) {
Some(host_display) => match *host_display.lock() {
#[cfg(feature = "vulkan_display")]
HostDisplayWrapper::Initialized(ref mut host_display) => {
match external_display_resource {
DisplayExternalResourceImport::VulkanImage {
descriptor,
metadata,
} => {
host_display.import_image(import_id, descriptor, metadata)?;
}
DisplayExternalResourceImport::VulkanTimelineSemaphore { descriptor } => {
host_display.import_semaphore(import_id, descriptor)?;
}
DisplayExternalResourceImport::Dmabuf { .. } => {
bail!("gpu_display_win does not support importing dmabufs")
}
DisplayExternalResourceImport::AHardwareBuffer { .. } => {
bail!("gpu_display_win does not support importing AHardwareBuffers")
}
}
Ok(())
}
HostDisplayWrapper::Uninitialized => {
bail!("HostDisplay is not initialized for this surface")
}
},
None => {
bail!("No HostDisplay for surface id {}", surface_id)
}
}
}
#[allow(unused_variables)]
fn release_import(&mut self, surface_id: u32, import_id: u32) {
#[cfg(feature = "vulkan_display")]
if let Some(host_display) = self.host_displays.get(&surface_id) {
if let HostDisplayWrapper::Initialized(ref mut host_display) = *host_display.lock() {
host_display.delete_imported_image_or_semaphore(import_id);
}
}
}
}
impl SysDisplayT for DisplayWin {
fn import_event_device(
&mut self,
event_device_id: u32,
event_device: EventDevice,
) -> GpuDisplayResult<()> {
self.import_event_device_internal(event_device_id, event_device)
.map_err(|e| {
GpuDisplayError::FailedEventDeviceImport(format!(
"Failed to import event device (ID: {event_device_id}): {e:?}"
))
})
}
fn handle_event_device(&mut self, event_device_id: u32) {
match ObjectId::new(event_device_id) {
Some(event_device_id) => {
if let Err(e) = self
.wndproc_thread
.post_display_command(DisplaySendToWndProc::HandleEventDevice(event_device_id))
{
error!(
"Failed to route guest -> host input_event; event device (ID: {:?}): {:?}",
event_device_id, e
);
}
}
None => error!(
"Failed to route guest -> host input_event; {} cannot be converted to ObjectId",
event_device_id
),
}
}
}
pub(crate) struct SurfaceWin {
surface_id: u32,
wndproc_thread: std::rc::Weak<WindowProcedureThread>,
close_requested_event: Event,
#[allow(dead_code)]
host_display: Arc<Mutex<HostDisplayWrapper>>,
}
impl GpuDisplaySurface for SurfaceWin {
fn close_requested(&self) -> bool {
match self
.close_requested_event
.wait_timeout(Duration::from_secs(0))
{
Ok(EventWaitResult::Signaled) => true,
Ok(EventWaitResult::TimedOut) => false,
Err(e) => {
error!("Failed to read whether display is closed: {}", e);
false
}
}
}
fn set_mouse_mode(&mut self, mouse_mode: MouseMode) {
if let Some(wndproc_thread) = self.wndproc_thread.upgrade() {
if let Err(e) =
wndproc_thread.post_display_command(DisplaySendToWndProc::SetMouseMode {
surface_id: self.surface_id,
mouse_mode,
})
{
warn!("Failed to post SetMouseMode message: {:?}", e);
}
}
}
#[cfg(not(feature = "vulkan_display"))]
fn flip_to(
&mut self,
_import_id: u32,
_acquire_timepoint: Option<SemaphoreTimepoint>,
_release_timepoint: Option<SemaphoreTimepoint>,
_extra_info: Option<FlipToExtraInfo>,
) -> Result<Waitable> {
bail!("host_display feature is not enabled")
}
#[cfg(feature = "vulkan_display")]
fn flip_to(
&mut self,
import_id: u32,
acquire_timepoint: Option<SemaphoreTimepoint>,
release_timepoint: Option<SemaphoreTimepoint>,
extra_info: Option<FlipToExtraInfo>,
) -> Result<Waitable> {
let last_layout_transition = match extra_info {
Some(FlipToExtraInfo::Vulkan {
old_layout,
new_layout,
}) => (old_layout, new_layout),
None => {
bail!("vulkan display flip_to requires old and new layout in extra_info")
}
};
let release_timepoint =
release_timepoint.ok_or(anyhow::anyhow!("release timepoint must be non-None"))?;
match *self.host_display.lock() {
HostDisplayWrapper::Initialized(ref mut host_display) => host_display.post(
import_id,
last_layout_transition,
acquire_timepoint,
release_timepoint,
),
HostDisplayWrapper::Uninitialized => {
bail!("HostDisplay is not initialized for this surface")
}
}
}
}
impl Drop for SurfaceWin {
fn drop(&mut self) {
info!("Dropping surface {}", self.surface_id);
if let Some(wndproc_thread) = self.wndproc_thread.upgrade() {
if let Err(e) =
wndproc_thread.post_display_command(DisplaySendToWndProc::ReleaseSurface {
surface_id: self.surface_id,
})
{
warn!(
"Failed to post ReleaseSurface message (benign if message loop has \
shut down): {:?}",
e
);
}
}
}
}
#[cfg(test)]
mod tests {
use base::Tube;
use super::*;
#[test]
fn can_create_2_window_proc_threads() {
let threads = (0..2)
.map(|_| {
let (main_ime_tube, _device_ime_tube) =
Tube::pair().expect("failed to create IME tube");
let wndproc_thread_builder = WindowProcedureThread::builder();
#[cfg(feature = "kiwi")]
let wndproc_thread_builder = {
let mut wndproc_thread_builder = wndproc_thread_builder;
wndproc_thread_builder
.set_max_num_windows(1)
.set_display_tube(None)
.set_ime_tube(Some(_device_ime_tube));
wndproc_thread_builder
};
(
wndproc_thread_builder.start_thread().unwrap(),
main_ime_tube,
)
})
.collect::<Vec<_>>();
drop(threads);
}
}