gpu_display/
gpu_display_wl.rs

1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Crate for displaying simple surfaces and GPU buffers over wayland.
6
7extern crate base;
8
9#[path = "dwl.rs"]
10#[allow(dead_code)]
11mod dwl;
12
13use std::cell::Cell;
14use std::cmp::max;
15use std::collections::HashMap;
16use std::ffi::CStr;
17use std::ffi::CString;
18use std::mem::zeroed;
19use std::panic::catch_unwind;
20use std::path::Path;
21use std::process::abort;
22use std::ptr::null;
23
24use anyhow::bail;
25use base::error;
26use base::round_up_to_page_size;
27use base::AsRawDescriptor;
28use base::MemoryMapping;
29use base::MemoryMappingBuilder;
30use base::RawDescriptor;
31use base::SharedMemory;
32use base::VolatileMemory;
33use dwl::*;
34use linux_input_sys::virtio_input_event;
35use sync::Waitable;
36use vm_control::gpu::DisplayParameters;
37
38use crate::DisplayExternalResourceImport;
39use crate::DisplayT;
40use crate::EventDeviceKind;
41use crate::FlipToExtraInfo;
42use crate::GpuDisplayError;
43use crate::GpuDisplayEvents;
44use crate::GpuDisplayFramebuffer;
45use crate::GpuDisplayResult;
46use crate::GpuDisplaySurface;
47use crate::SemaphoreTimepoint;
48use crate::SurfaceType;
49use crate::SysDisplayT;
50
51const BUFFER_COUNT: usize = 3;
52const BYTES_PER_PIXEL: u32 = 4;
53
54struct DwlContext(*mut dwl_context);
55impl Drop for DwlContext {
56    fn drop(&mut self) {
57        if !self.0.is_null() {
58            // SAFETY:
59            // Safe given that we checked the pointer for non-null and it should always be of the
60            // correct type.
61            unsafe {
62                dwl_context_destroy(&mut self.0);
63            }
64        }
65    }
66}
67
68impl AsRawDescriptor for DwlContext {
69    fn as_raw_descriptor(&self) -> RawDescriptor {
70        // SAFETY:
71        // Safe given that the context pointer is valid.
72        unsafe { dwl_context_fd(self.0) }
73    }
74}
75
76struct DwlDmabuf(*mut dwl_dmabuf);
77
78impl Drop for DwlDmabuf {
79    fn drop(&mut self) {
80        if !self.0.is_null() {
81            // SAFETY:
82            // Safe given that we checked the pointer for non-null and it should always be of the
83            // correct type.
84            unsafe {
85                dwl_dmabuf_destroy(&mut self.0);
86            }
87        }
88    }
89}
90
91struct DwlSurface(*mut dwl_surface);
92impl Drop for DwlSurface {
93    fn drop(&mut self) {
94        if !self.0.is_null() {
95            // SAFETY:
96            // Safe given that we checked the pointer for non-null and it should always be of the
97            // correct type.
98            unsafe {
99                dwl_surface_destroy(&mut self.0);
100            }
101        }
102    }
103}
104
105struct WaylandSurface {
106    surface: DwlSurface,
107    row_size: u32,
108    buffer_size: usize,
109    buffer_index: Cell<usize>,
110    buffer_mem: MemoryMapping,
111}
112
113impl WaylandSurface {
114    fn surface(&self) -> *mut dwl_surface {
115        self.surface.0
116    }
117}
118
119impl GpuDisplaySurface for WaylandSurface {
120    fn surface_descriptor(&self) -> u64 {
121        // SAFETY:
122        // Safe if the surface is valid.
123        let pointer = unsafe { dwl_surface_descriptor(self.surface.0) };
124        pointer as u64
125    }
126
127    fn framebuffer(&mut self) -> Option<GpuDisplayFramebuffer> {
128        let buffer_index = (self.buffer_index.get() + 1) % BUFFER_COUNT;
129        let framebuffer = self
130            .buffer_mem
131            .get_slice(buffer_index * self.buffer_size, self.buffer_size)
132            .ok()?;
133
134        Some(GpuDisplayFramebuffer::new(
135            framebuffer,
136            self.row_size,
137            BYTES_PER_PIXEL,
138        ))
139    }
140
141    fn next_buffer_in_use(&self) -> bool {
142        let next_buffer_index = (self.buffer_index.get() + 1) % BUFFER_COUNT;
143        // SAFETY:
144        // Safe because only a valid surface and buffer index is used.
145        unsafe { dwl_surface_buffer_in_use(self.surface(), next_buffer_index) }
146    }
147
148    fn close_requested(&self) -> bool {
149        // SAFETY:
150        // Safe because only a valid surface is used.
151        unsafe { dwl_surface_close_requested(self.surface()) }
152    }
153
154    fn flip(&mut self) {
155        self.buffer_index
156            .set((self.buffer_index.get() + 1) % BUFFER_COUNT);
157
158        // SAFETY:
159        // Safe because only a valid surface and buffer index is used.
160        unsafe {
161            dwl_surface_flip(self.surface(), self.buffer_index.get());
162        }
163    }
164
165    fn flip_to(
166        &mut self,
167        import_id: u32,
168        _acquire_timepoint: Option<SemaphoreTimepoint>,
169        _release_timepoint: Option<SemaphoreTimepoint>,
170        _extra_info: Option<FlipToExtraInfo>,
171    ) -> anyhow::Result<Waitable> {
172        // SAFETY:
173        // Safe because only a valid surface and import_id is used.
174        unsafe { dwl_surface_flip_to(self.surface(), import_id) };
175        Ok(Waitable::signaled())
176    }
177
178    fn commit(&mut self) -> GpuDisplayResult<()> {
179        // SAFETY:
180        // Safe because only a valid surface is used.
181        unsafe {
182            dwl_surface_commit(self.surface());
183        }
184
185        Ok(())
186    }
187
188    fn set_position(&mut self, x: u32, y: u32) {
189        // SAFETY:
190        // Safe because only a valid surface is used.
191        unsafe {
192            dwl_surface_set_position(self.surface(), x, y);
193        }
194    }
195}
196
197/// A connection to the compositor and associated collection of state.
198///
199/// The user of `GpuDisplay` can use `AsRawDescriptor` to poll on the compositor connection's file
200/// descriptor. When the connection is readable, `dispatch_events` can be called to process it.
201pub struct DisplayWl {
202    dmabufs: HashMap<u32, DwlDmabuf>,
203    ctx: DwlContext,
204    current_event: Option<dwl_event>,
205    mt_tracking_id: u16,
206}
207
208/// Error logging callback used by wrapped C implementation.
209///
210/// # Safety
211///
212/// safe because it must be passed a valid pointer to null-terminated c-string.
213#[allow(clippy::unnecessary_cast)]
214unsafe extern "C" fn error_callback(message: *const ::std::os::raw::c_char) {
215    catch_unwind(|| {
216        assert!(!message.is_null());
217        // SAFETY: trivially safe
218        let msg = unsafe {
219            std::str::from_utf8(std::slice::from_raw_parts(
220                message as *const u8,
221                libc::strlen(message),
222            ))
223            .unwrap()
224        };
225        error!("{}", msg);
226    })
227    .unwrap_or_else(|_| abort())
228}
229
230impl DisplayWl {
231    /// Opens a fresh connection to the compositor.
232    pub fn new(wayland_path: Option<&Path>) -> GpuDisplayResult<DisplayWl> {
233        // SAFETY:
234        // The dwl_context_new call should always be safe to call, and we check its result.
235        let ctx = DwlContext(unsafe { dwl_context_new(Some(error_callback)) });
236        if ctx.0.is_null() {
237            return Err(GpuDisplayError::Allocate);
238        }
239
240        // The dwl_context_setup call is always safe to call given that the supplied context is
241        // valid. and we check its result.
242        let cstr_path = match wayland_path.map(|p| p.as_os_str().to_str()) {
243            Some(Some(s)) => match CString::new(s) {
244                Ok(cstr) => Some(cstr),
245                Err(_) => return Err(GpuDisplayError::InvalidPath),
246            },
247            Some(None) => return Err(GpuDisplayError::InvalidPath),
248            None => None,
249        };
250        // This grabs a pointer to cstr_path without moving the CString into the .map closure
251        // accidentally, which triggeres a really hard to catch use after free in
252        // dwl_context_setup.
253        let cstr_path_ptr = cstr_path
254            .as_ref()
255            .map(|s: &CString| CStr::as_ptr(s))
256            .unwrap_or(null());
257        // SAFETY: args are valid and the return value is checked.
258        let setup_success = unsafe { dwl_context_setup(ctx.0, cstr_path_ptr) };
259        if !setup_success {
260            return Err(GpuDisplayError::Connect);
261        }
262
263        Ok(DisplayWl {
264            dmabufs: HashMap::new(),
265            ctx,
266            current_event: None,
267            mt_tracking_id: 0u16,
268        })
269    }
270
271    fn ctx(&self) -> *mut dwl_context {
272        self.ctx.0
273    }
274
275    fn pop_event(&self) -> dwl_event {
276        // SAFETY:
277        // Safe because dwl_next_events from a context's circular buffer.
278        unsafe {
279            let mut ev = zeroed();
280            dwl_context_next_event(self.ctx(), &mut ev);
281            ev
282        }
283    }
284
285    fn next_tracking_id(&mut self) -> i32 {
286        let cur_id: i32 = self.mt_tracking_id as i32;
287        self.mt_tracking_id = self.mt_tracking_id.wrapping_add(1);
288        cur_id
289    }
290
291    fn current_tracking_id(&self) -> i32 {
292        self.mt_tracking_id as i32
293    }
294}
295
296impl DisplayT for DisplayWl {
297    fn pending_events(&self) -> bool {
298        // SAFETY:
299        // Safe because the function just queries the values of two variables in a context.
300        unsafe { dwl_context_pending_events(self.ctx()) }
301    }
302
303    fn next_event(&mut self) -> GpuDisplayResult<u64> {
304        let ev = self.pop_event();
305        let descriptor = ev.surface_descriptor as u64;
306        self.current_event = Some(ev);
307        Ok(descriptor)
308    }
309
310    fn handle_next_event(
311        &mut self,
312        _surface: &mut Box<dyn GpuDisplaySurface>,
313    ) -> Option<GpuDisplayEvents> {
314        // Should not panic since the common layer only calls this when an event occurs.
315        let event = self.current_event.take().unwrap();
316
317        match event.event_type {
318            DWL_EVENT_TYPE_KEYBOARD_ENTER => None,
319            DWL_EVENT_TYPE_KEYBOARD_LEAVE => None,
320            DWL_EVENT_TYPE_KEYBOARD_KEY => {
321                let linux_keycode = event.params[0] as u16;
322                let pressed = event.params[1] == DWL_KEYBOARD_KEY_STATE_PRESSED;
323                let events = vec![virtio_input_event::key(linux_keycode, pressed, false)];
324                Some(GpuDisplayEvents {
325                    events,
326                    device_type: EventDeviceKind::Keyboard,
327                })
328            }
329            // TODO(tutankhamen): slot is always 0, because all the input
330            // events come from mouse device, i.e. only one touch is possible at a time.
331            // Full MT protocol has to be implemented and properly wired later.
332            DWL_EVENT_TYPE_TOUCH_DOWN | DWL_EVENT_TYPE_TOUCH_MOTION => {
333                let tracking_id = if event.event_type == DWL_EVENT_TYPE_TOUCH_DOWN {
334                    self.next_tracking_id()
335                } else {
336                    self.current_tracking_id()
337                };
338
339                let events = vec![
340                    virtio_input_event::multitouch_slot(0),
341                    virtio_input_event::multitouch_tracking_id(tracking_id),
342                    virtio_input_event::multitouch_absolute_x(max(0, event.params[0])),
343                    virtio_input_event::multitouch_absolute_y(max(0, event.params[1])),
344                    virtio_input_event::touch(true),
345                ];
346                Some(GpuDisplayEvents {
347                    events,
348                    device_type: EventDeviceKind::Touchscreen,
349                })
350            }
351            DWL_EVENT_TYPE_TOUCH_UP => {
352                let events = vec![
353                    virtio_input_event::multitouch_slot(0),
354                    virtio_input_event::multitouch_tracking_id(-1),
355                    virtio_input_event::touch(false),
356                ];
357                Some(GpuDisplayEvents {
358                    events,
359                    device_type: EventDeviceKind::Touchscreen,
360                })
361            }
362            _ => {
363                error!("unknown event type {}", event.event_type);
364                None
365            }
366        }
367    }
368
369    fn flush(&self) {
370        // SAFETY:
371        // Safe given that the context pointer is valid.
372        unsafe {
373            dwl_context_dispatch(self.ctx());
374        }
375    }
376
377    fn create_surface(
378        &mut self,
379        parent_surface_id: Option<u32>,
380        surface_id: u32,
381        scanout_id: Option<u32>,
382        display_params: &DisplayParameters,
383        surf_type: SurfaceType,
384    ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
385        let parent_id = parent_surface_id.unwrap_or(0);
386
387        let (width, height) = display_params.get_virtual_display_size();
388        let row_size = width * BYTES_PER_PIXEL;
389        let fb_size = row_size * height;
390        let buffer_size = round_up_to_page_size(fb_size as usize * BUFFER_COUNT);
391        let buffer_shm = SharedMemory::new("GpuDisplaySurface", buffer_size as u64)?;
392        let buffer_mem = MemoryMappingBuilder::new(buffer_size)
393            .from_shared_memory(&buffer_shm)
394            .build()
395            .unwrap();
396
397        let dwl_surf_flags = match surf_type {
398            SurfaceType::Cursor => DWL_SURFACE_FLAG_HAS_ALPHA,
399            SurfaceType::Scanout => DWL_SURFACE_FLAG_RECEIVE_INPUT,
400        };
401        // SAFETY:
402        // Safe because only a valid context, parent ID (if not non-zero), and buffer FD are used.
403        // The returned surface is checked for validity before being filed away.
404        let surface = DwlSurface(unsafe {
405            dwl_context_surface_new(
406                self.ctx(),
407                parent_id,
408                surface_id,
409                buffer_shm.as_raw_descriptor(),
410                buffer_size,
411                fb_size as usize,
412                width,
413                height,
414                row_size,
415                dwl_surf_flags,
416            )
417        });
418
419        if surface.0.is_null() {
420            return Err(GpuDisplayError::CreateSurface);
421        }
422
423        if let Some(scanout_id) = scanout_id {
424            // SAFETY:
425            // Safe because only a valid surface is used.
426            unsafe {
427                dwl_surface_set_scanout_id(surface.0, scanout_id);
428            }
429        }
430
431        Ok(Box::new(WaylandSurface {
432            surface,
433            row_size,
434            buffer_size: fb_size as usize,
435            buffer_index: Cell::new(0),
436            buffer_mem,
437        }))
438    }
439
440    fn import_resource(
441        &mut self,
442        import_id: u32,
443        _surface_id: u32,
444        external_display_resource: DisplayExternalResourceImport,
445    ) -> anyhow::Result<()> {
446        // This let pattern is always true if the host_display feature is disabled.
447        #[allow(irrefutable_let_patterns)]
448        if let DisplayExternalResourceImport::Dmabuf {
449            descriptor,
450            offset,
451            stride,
452            modifiers,
453            width,
454            height,
455            fourcc,
456        } = external_display_resource
457        {
458            // SAFETY:
459            // Safe given that the context pointer is valid. Any other invalid parameters would be
460            // rejected by dwl_context_dmabuf_new safely. We check that the resulting dmabuf is
461            // valid before filing it away.
462            let dmabuf = DwlDmabuf(unsafe {
463                dwl_context_dmabuf_new(
464                    self.ctx(),
465                    import_id,
466                    descriptor.as_raw_descriptor(),
467                    offset,
468                    stride,
469                    modifiers,
470                    width,
471                    height,
472                    fourcc,
473                )
474            });
475
476            if dmabuf.0.is_null() {
477                bail!("dmabuf import failed.");
478            }
479
480            self.dmabufs.insert(import_id, dmabuf);
481
482            Ok(())
483        } else {
484            bail!("gpu_display_wl only supports Dmabuf imports");
485        }
486    }
487
488    fn release_import(&mut self, _surface_id: u32, import_id: u32) {
489        self.dmabufs.remove(&import_id);
490    }
491}
492
493impl SysDisplayT for DisplayWl {}
494
495impl AsRawDescriptor for DisplayWl {
496    fn as_raw_descriptor(&self) -> RawDescriptor {
497        // Safe given that the context pointer is valid.
498        self.ctx.as_raw_descriptor()
499    }
500}