// Copyright 2019 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 wayland. extern crate base; #[path = "dwl.rs"] #[allow(dead_code)] mod dwl; use std::cell::Cell; use std::cmp::max; use std::collections::HashMap; use std::ffi::CStr; use std::ffi::CString; use std::mem::zeroed; use std::panic::catch_unwind; use std::path::Path; use std::process::abort; use std::ptr::null; use anyhow::bail; use base::error; use base::round_up_to_page_size; use base::AsRawDescriptor; use base::MemoryMapping; use base::MemoryMappingBuilder; use base::RawDescriptor; use base::SharedMemory; use base::VolatileMemory; use dwl::*; use linux_input_sys::virtio_input_event; use sync::Waitable; use vm_control::gpu::DisplayParameters; use crate::DisplayExternalResourceImport; use crate::DisplayT; use crate::EventDeviceKind; use crate::FlipToExtraInfo; use crate::GpuDisplayError; use crate::GpuDisplayEvents; use crate::GpuDisplayFramebuffer; use crate::GpuDisplayResult; use crate::GpuDisplaySurface; use crate::SemaphoreTimepoint; use crate::SurfaceType; use crate::SysDisplayT; const BUFFER_COUNT: usize = 3; const BYTES_PER_PIXEL: u32 = 4; struct DwlContext(*mut dwl_context); impl Drop for DwlContext { fn drop(&mut self) { if !self.0.is_null() { // SAFETY: // Safe given that we checked the pointer for non-null and it should always be of the // correct type. unsafe { dwl_context_destroy(&mut self.0); } } } } impl AsRawDescriptor for DwlContext { fn as_raw_descriptor(&self) -> RawDescriptor { // SAFETY: // Safe given that the context pointer is valid. unsafe { dwl_context_fd(self.0) } } } struct DwlDmabuf(*mut dwl_dmabuf); impl Drop for DwlDmabuf { fn drop(&mut self) { if !self.0.is_null() { // SAFETY: // Safe given that we checked the pointer for non-null and it should always be of the // correct type. unsafe { dwl_dmabuf_destroy(&mut self.0); } } } } struct DwlSurface(*mut dwl_surface); impl Drop for DwlSurface { fn drop(&mut self) { if !self.0.is_null() { // SAFETY: // Safe given that we checked the pointer for non-null and it should always be of the // correct type. unsafe { dwl_surface_destroy(&mut self.0); } } } } struct WaylandSurface { surface: DwlSurface, row_size: u32, buffer_size: usize, buffer_index: Cell, buffer_mem: MemoryMapping, } impl WaylandSurface { fn surface(&self) -> *mut dwl_surface { self.surface.0 } } impl GpuDisplaySurface for WaylandSurface { fn surface_descriptor(&self) -> u64 { // SAFETY: // Safe if the surface is valid. let pointer = unsafe { dwl_surface_descriptor(self.surface.0) }; pointer as u64 } fn framebuffer(&mut self) -> Option { let buffer_index = (self.buffer_index.get() + 1) % BUFFER_COUNT; let framebuffer = self .buffer_mem .get_slice(buffer_index * self.buffer_size, self.buffer_size) .ok()?; Some(GpuDisplayFramebuffer::new( framebuffer, self.row_size, BYTES_PER_PIXEL, )) } fn next_buffer_in_use(&self) -> bool { let next_buffer_index = (self.buffer_index.get() + 1) % BUFFER_COUNT; // SAFETY: // Safe because only a valid surface and buffer index is used. unsafe { dwl_surface_buffer_in_use(self.surface(), next_buffer_index) } } fn close_requested(&self) -> bool { // SAFETY: // Safe because only a valid surface is used. unsafe { dwl_surface_close_requested(self.surface()) } } fn flip(&mut self) { self.buffer_index .set((self.buffer_index.get() + 1) % BUFFER_COUNT); // SAFETY: // Safe because only a valid surface and buffer index is used. unsafe { dwl_surface_flip(self.surface(), self.buffer_index.get()); } } fn flip_to( &mut self, import_id: u32, _acquire_timepoint: Option, _release_timepoint: Option, _extra_info: Option, ) -> anyhow::Result { // SAFETY: // Safe because only a valid surface and import_id is used. unsafe { dwl_surface_flip_to(self.surface(), import_id) }; Ok(Waitable::signaled()) } fn commit(&mut self) -> GpuDisplayResult<()> { // SAFETY: // Safe because only a valid surface is used. unsafe { dwl_surface_commit(self.surface()); } Ok(()) } fn set_position(&mut self, x: u32, y: u32) { // SAFETY: // Safe because only a valid surface is used. unsafe { dwl_surface_set_position(self.surface(), x, y); } } } /// 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 DisplayWl { dmabufs: HashMap, ctx: DwlContext, current_event: Option, mt_tracking_id: u16, } /// Error logging callback used by wrapped C implementation. /// /// # Safety /// /// safe because it must be passed a valid pointer to null-terminated c-string. #[allow(clippy::unnecessary_cast)] unsafe extern "C" fn error_callback(message: *const ::std::os::raw::c_char) { catch_unwind(|| { assert!(!message.is_null()); // SAFETY: trivially safe let msg = unsafe { std::str::from_utf8(std::slice::from_raw_parts( message as *const u8, libc::strlen(message), )) .unwrap() }; error!("{}", msg); }) .unwrap_or_else(|_| abort()) } impl DisplayWl { /// Opens a fresh connection to the compositor. pub fn new(wayland_path: Option<&Path>) -> GpuDisplayResult { // SAFETY: // The dwl_context_new call should always be safe to call, and we check its result. let ctx = DwlContext(unsafe { dwl_context_new(Some(error_callback)) }); if ctx.0.is_null() { return Err(GpuDisplayError::Allocate); } // The dwl_context_setup call is always safe to call given that the supplied context is // valid. and we check its result. let cstr_path = match wayland_path.map(|p| p.as_os_str().to_str()) { Some(Some(s)) => match CString::new(s) { Ok(cstr) => Some(cstr), Err(_) => return Err(GpuDisplayError::InvalidPath), }, Some(None) => return Err(GpuDisplayError::InvalidPath), None => None, }; // This grabs a pointer to cstr_path without moving the CString into the .map closure // accidentally, which triggeres a really hard to catch use after free in // dwl_context_setup. let cstr_path_ptr = cstr_path .as_ref() .map(|s: &CString| CStr::as_ptr(s)) .unwrap_or(null()); // SAFETY: args are valid and the return value is checked. let setup_success = unsafe { dwl_context_setup(ctx.0, cstr_path_ptr) }; if !setup_success { return Err(GpuDisplayError::Connect); } Ok(DisplayWl { dmabufs: HashMap::new(), ctx, current_event: None, mt_tracking_id: 0u16, }) } fn ctx(&self) -> *mut dwl_context { self.ctx.0 } fn pop_event(&self) -> dwl_event { // SAFETY: // Safe because dwl_next_events from a context's circular buffer. unsafe { let mut ev = zeroed(); dwl_context_next_event(self.ctx(), &mut ev); ev } } fn next_tracking_id(&mut self) -> i32 { let cur_id: i32 = self.mt_tracking_id as i32; self.mt_tracking_id = self.mt_tracking_id.wrapping_add(1); cur_id } fn current_tracking_id(&self) -> i32 { self.mt_tracking_id as i32 } } impl DisplayT for DisplayWl { fn pending_events(&self) -> bool { // SAFETY: // Safe because the function just queries the values of two variables in a context. unsafe { dwl_context_pending_events(self.ctx()) } } fn next_event(&mut self) -> GpuDisplayResult { let ev = self.pop_event(); let descriptor = ev.surface_descriptor as u64; self.current_event = Some(ev); Ok(descriptor) } fn handle_next_event( &mut self, _surface: &mut Box, ) -> Option { // Should not panic since the common layer only calls this when an event occurs. let event = self.current_event.take().unwrap(); match event.event_type { DWL_EVENT_TYPE_KEYBOARD_ENTER => None, DWL_EVENT_TYPE_KEYBOARD_LEAVE => None, DWL_EVENT_TYPE_KEYBOARD_KEY => { let linux_keycode = event.params[0] as u16; let pressed = event.params[1] == DWL_KEYBOARD_KEY_STATE_PRESSED; let events = vec![virtio_input_event::key(linux_keycode, pressed, false)]; Some(GpuDisplayEvents { events, device_type: EventDeviceKind::Keyboard, }) } // TODO(tutankhamen): slot is always 0, because all the input // events come from mouse device, i.e. only one touch is possible at a time. // Full MT protocol has to be implemented and properly wired later. DWL_EVENT_TYPE_TOUCH_DOWN | DWL_EVENT_TYPE_TOUCH_MOTION => { let tracking_id = if event.event_type == DWL_EVENT_TYPE_TOUCH_DOWN { self.next_tracking_id() } else { self.current_tracking_id() }; let events = vec![ virtio_input_event::multitouch_slot(0), virtio_input_event::multitouch_tracking_id(tracking_id), virtio_input_event::multitouch_absolute_x(max(0, event.params[0])), virtio_input_event::multitouch_absolute_y(max(0, event.params[1])), virtio_input_event::touch(true), ]; Some(GpuDisplayEvents { events, device_type: EventDeviceKind::Touchscreen, }) } DWL_EVENT_TYPE_TOUCH_UP => { let events = vec![ virtio_input_event::multitouch_slot(0), virtio_input_event::multitouch_tracking_id(-1), virtio_input_event::touch(false), ]; Some(GpuDisplayEvents { events, device_type: EventDeviceKind::Touchscreen, }) } _ => { error!("unknown event type {}", event.event_type); None } } } fn flush(&self) { // SAFETY: // Safe given that the context pointer is valid. unsafe { dwl_context_dispatch(self.ctx()); } } fn create_surface( &mut self, parent_surface_id: Option, surface_id: u32, scanout_id: Option, display_params: &DisplayParameters, surf_type: SurfaceType, ) -> GpuDisplayResult> { let parent_id = parent_surface_id.unwrap_or(0); let (width, height) = display_params.get_virtual_display_size(); let row_size = width * BYTES_PER_PIXEL; let fb_size = row_size * height; let buffer_size = round_up_to_page_size(fb_size as usize * BUFFER_COUNT); let buffer_shm = SharedMemory::new("GpuDisplaySurface", buffer_size as u64)?; let buffer_mem = MemoryMappingBuilder::new(buffer_size) .from_shared_memory(&buffer_shm) .build() .unwrap(); let dwl_surf_flags = match surf_type { SurfaceType::Cursor => DWL_SURFACE_FLAG_HAS_ALPHA, SurfaceType::Scanout => DWL_SURFACE_FLAG_RECEIVE_INPUT, }; // SAFETY: // Safe because only a valid context, parent ID (if not non-zero), and buffer FD are used. // The returned surface is checked for validity before being filed away. let surface = DwlSurface(unsafe { dwl_context_surface_new( self.ctx(), parent_id, surface_id, buffer_shm.as_raw_descriptor(), buffer_size, fb_size as usize, width, height, row_size, dwl_surf_flags, ) }); if surface.0.is_null() { return Err(GpuDisplayError::CreateSurface); } if let Some(scanout_id) = scanout_id { // SAFETY: // Safe because only a valid surface is used. unsafe { dwl_surface_set_scanout_id(surface.0, scanout_id); } } Ok(Box::new(WaylandSurface { surface, row_size, buffer_size: fb_size as usize, buffer_index: Cell::new(0), buffer_mem, })) } fn import_resource( &mut self, import_id: u32, _surface_id: u32, external_display_resource: DisplayExternalResourceImport, ) -> anyhow::Result<()> { // This let pattern is always true if the vulkan_display feature is disabled. #[allow(irrefutable_let_patterns)] if let DisplayExternalResourceImport::Dmabuf { descriptor, offset, stride, modifiers, width, height, fourcc, } = external_display_resource { // SAFETY: // Safe given that the context pointer is valid. Any other invalid parameters would be // rejected by dwl_context_dmabuf_new safely. We check that the resulting dmabuf is // valid before filing it away. let dmabuf = DwlDmabuf(unsafe { dwl_context_dmabuf_new( self.ctx(), import_id, descriptor.as_raw_descriptor(), offset, stride, modifiers, width, height, fourcc, ) }); if dmabuf.0.is_null() { bail!("dmabuf import failed."); } self.dmabufs.insert(import_id, dmabuf); Ok(()) } else { bail!("gpu_display_wl only supports Dmabuf imports"); } } fn release_import(&mut self, _surface_id: u32, import_id: u32) { self.dmabufs.remove(&import_id); } } impl SysDisplayT for DisplayWl {} impl AsRawDescriptor for DisplayWl { fn as_raw_descriptor(&self) -> RawDescriptor { // Safe given that the context pointer is valid. self.ctx.as_raw_descriptor() } }