summary refs log blame commit diff
path: root/gpu_display/src/gpu_display_x.rs
blob: 0882dc974a8be065256d722347831714f7b9b6bc (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                                                                         
                                        
                  






                                        
                
                        


                                                                           



                                                                                                 

                              
                                                              


                              
                           

























































































                                                                                        









                                                                                              
















                                                                                                   





                                  
































































                                                                                              

                                                   


















                                                 
                                                                                   










































                                                                                               









                                                      














                                                                          

                                                  
















                                                                                      




                                      









                                                                              

                                                       

                                                                                             
                                                           













                                                                                                


                                                                                      






                                                                                         


                                                                                



                                                                                         






















































































































































                                                                                                   





                                         
                     
                                             


                              


                                                   



                                                                                     

                                                                                  
                                                            
















                                                                     



                                                          








































                                                                                      
                         


                        
                                                   
                                             
                                                  




                                                                
                                                                            


                                                                        








                                                                                          











                                                   
 
                                           









                                               















































                                                                                                   
















                                                     
                                          
                                                          
                                                                      




                                                    




                                                                              


































                                                                                 
                                         
















                                                                 
















                                                                                                  
     


                                                                                     
     
 
                                                                              










                                                                                
     



                                  
                                 

     
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#[path = "generated/xlib.rs"]
#[allow(
    dead_code,
    non_snake_case,
    non_camel_case_types,
    non_upper_case_globals
)]
mod xlib;

use linux_input_sys::virtio_input_event;
use std::cmp::max;
use std::collections::BTreeMap;
use std::ffi::{c_void, CStr, CString};
use std::mem::{transmute_copy, zeroed};
use std::num::NonZeroU32;
use std::os::raw::c_ulong;
use std::os::unix::io::{AsRawFd, RawFd};
use std::ptr::{null, null_mut, NonNull};
use std::rc::Rc;
use std::time::Duration;

use libc::{shmat, shmctl, shmdt, shmget, IPC_CREAT, IPC_PRIVATE, IPC_RMID};

use crate::{
    keycode_converter::KeycodeTranslator, keycode_converter::KeycodeTypes, DisplayT, EventDevice,
    EventDeviceKind, GpuDisplayError, GpuDisplayFramebuffer,
};

use data_model::VolatileSlice;
use sys_util::{error, PollContext, PollToken, WatchingEvents};

const BUFFER_COUNT: usize = 2;

type ObjectId = NonZeroU32;

/// A wrapper for XFree that takes any type.
unsafe fn x_free<T>(t: *mut T) {
    xlib::XFree(t as *mut c_void);
}

#[derive(Clone)]
struct XDisplay(Rc<NonNull<xlib::Display>>);
impl Drop for XDisplay {
    fn drop(&mut self) {
        if Rc::strong_count(&self.0) == 1 {
            unsafe {
                xlib::XCloseDisplay(self.as_ptr());
            }
        }
    }
}

impl XDisplay {
    fn as_ptr(&self) -> *mut xlib::Display {
        self.0.as_ptr()
    }

    /// Returns true of the XShm extension is supported on this display.
    fn supports_shm(&self) -> bool {
        unsafe { xlib::XShmQueryExtension(self.as_ptr()) != 0 }
    }

    /// Gets the default screen of this display.
    fn default_screen(&self) -> Option<XScreen> {
        Some(XScreen(NonNull::new(unsafe {
            xlib::XDefaultScreenOfDisplay(self.as_ptr())
        })?))
    }

    /// Returns true if there are events that are on the queue.
    fn pending_events(&self) -> bool {
        unsafe { xlib::XPending(self.as_ptr()) != 0 }
    }

    /// Sends any pending commands to the X server.
    fn flush(&self) {
        unsafe {
            xlib::XFlush(self.as_ptr());
        }
    }

    /// Blocks until the next event from the display is received and returns that event.
    ///
    /// Always flush before using this if any X commands where issued.
    fn next_event(&self) -> XEvent {
        unsafe {
            let mut ev = zeroed();
            xlib::XNextEvent(self.as_ptr(), &mut ev);
            ev.into()
        }
    }
}

impl AsRawFd for XDisplay {
    fn as_raw_fd(&self) -> RawFd {
        unsafe { xlib::XConnectionNumber(self.as_ptr()) }
    }
}

struct XEvent(xlib::XEvent);
impl From<xlib::XEvent> for XEvent {
    fn from(ev: xlib::XEvent) -> XEvent {
        XEvent(ev)
    }
}

impl XEvent {
    fn any(&self) -> xlib::XAnyEvent {
        // All events have the same xany field.
        unsafe { self.0.xany }
    }

    fn type_(&self) -> u32 {
        // All events have the same type_ field.
        unsafe { self.0.type_ as u32 }
    }

    fn window(&self) -> xlib::Window {
        self.any().window
    }

    // Some of the event types are dynamic so they need to be passed in.
    fn as_enum(&self, shm_complete_type: u32) -> XEventEnum {
        match self.type_() {
            xlib::KeyPress | xlib::KeyRelease => XEventEnum::KeyEvent(unsafe { self.0.xkey }),
            xlib::ButtonPress => XEventEnum::ButtonEvent {
                event: unsafe { self.0.xbutton },
                pressed: true,
            },
            xlib::ButtonRelease => XEventEnum::ButtonEvent {
                event: unsafe { self.0.xbutton },
                pressed: false,
            },
            xlib::MotionNotify => XEventEnum::Motion(unsafe { self.0.xmotion }),
            xlib::Expose => XEventEnum::Expose,
            xlib::ClientMessage => {
                XEventEnum::ClientMessage(unsafe { self.0.xclient.data.l[0] as u64 })
            }
            t if t == shm_complete_type => {
                // Because XShmCompletionEvent is not part of the XEvent union, simulate a union
                // with transmute_copy. If the shm_complete_type turns out to be bogus, some of the
                // data would be incorrect, but the common event fields would still be valid.
                let ev_completion: xlib::XShmCompletionEvent = unsafe { transmute_copy(&self.0) };
                XEventEnum::ShmCompletionEvent(ev_completion.shmseg)
            }
            _ => XEventEnum::Unhandled,
        }
    }
}

enum XEventEnum {
    KeyEvent(xlib::XKeyEvent),
    ButtonEvent {
        event: xlib::XButtonEvent,
        pressed: bool,
    },
    Motion(xlib::XMotionEvent),
    Expose,
    ClientMessage(u64),
    ShmCompletionEvent(xlib::ShmSeg),
    // We don't care about most kinds of events,
    Unhandled,
}

struct XScreen(NonNull<xlib::Screen>);

impl XScreen {
    fn as_ptr(&self) -> *mut xlib::Screen {
        self.0.as_ptr()
    }

    /// Gets the screen number of this screen.
    fn get_number(&self) -> i32 {
        unsafe { xlib::XScreenNumberOfScreen(self.as_ptr()) }
    }
}

struct Buffer {
    display: XDisplay,
    image: *mut xlib::XImage,
    /// The documentation says XShmSegmentInfo must last at least as long as the XImage, which
    /// probably precludes moving it as well.
    segment_info: Box<xlib::XShmSegmentInfo>,
    size: usize,
    in_use: bool,
}

impl Drop for Buffer {
    fn drop(&mut self) {
        unsafe {
            xlib::XShmDetach(self.display.as_ptr(), self.segment_info.as_mut());
            xlib::XDestroyImage(self.image);
            shmdt(self.segment_info.shmaddr as *const _);
            shmctl(self.segment_info.shmid, IPC_RMID, null_mut());
        }
    }
}

impl Buffer {
    fn as_volatile_slice(&self) -> VolatileSlice {
        unsafe { VolatileSlice::new(self.segment_info.shmaddr as *mut _, self.size as u64) }
    }

    fn stride(&self) -> usize {
        unsafe { (*self.image).bytes_per_line as usize }
    }

    fn bytes_per_pixel(&self) -> usize {
        let bytes_per_pixel = unsafe { (*self.image).bits_per_pixel / 8 };
        bytes_per_pixel as usize
    }
}

// Surfaces here are equivalent to XWindows.
struct Surface {
    display: XDisplay,
    visual: *mut xlib::Visual,
    depth: u32,
    window: xlib::Window,
    gc: xlib::GC,
    width: u32,
    height: u32,
    event_devices: BTreeMap<ObjectId, EventDevice>,
    keycode_translator: KeycodeTranslator,

    // Fields for handling the buffer swap chain.
    buffers: [Option<Buffer>; BUFFER_COUNT],
    buffer_next: usize,
    buffer_completion_type: u32,

    // Fields for handling window close requests
    delete_window_atom: c_ulong,
    close_requested: bool,
}

impl Surface {
    fn create(
        display: XDisplay,
        screen: &XScreen,
        visual: *mut xlib::Visual,
        width: u32,
        height: u32,
    ) -> Result<Surface, GpuDisplayError> {
        let keycode_translator = KeycodeTranslator::new(KeycodeTypes::XkbScancode);
        unsafe {
            let depth = xlib::XDefaultDepthOfScreen(screen.as_ptr()) as u32;

            let black_pixel = xlib::XBlackPixelOfScreen(screen.as_ptr());

            let window = xlib::XCreateSimpleWindow(
                display.as_ptr(),
                xlib::XRootWindowOfScreen(screen.as_ptr()),
                0,
                0,
                width,
                height,
                1,
                black_pixel,
                black_pixel,
            );

            let gc = xlib::XCreateGC(display.as_ptr(), window, 0, null_mut());

            // Because the event is from an extension, its type must be calculated dynamically.
            let buffer_completion_type =
                xlib::XShmGetEventBase(display.as_ptr()) as u32 + xlib::ShmCompletion;

            // Mark this window as responding to close requests.
            let mut delete_window_atom = xlib::XInternAtom(
                display.as_ptr(),
                CStr::from_bytes_with_nul(b"WM_DELETE_WINDOW\0")
                    .unwrap()
                    .as_ptr(),
                0,
            );
            xlib::XSetWMProtocols(display.as_ptr(), window, &mut delete_window_atom, 1);

            let size_hints = xlib::XAllocSizeHints();
            (*size_hints).flags = (xlib::PMinSize | xlib::PMaxSize) as i64;
            (*size_hints).max_width = width as i32;
            (*size_hints).min_width = width as i32;
            (*size_hints).max_height = height as i32;
            (*size_hints).min_height = height as i32;
            xlib::XSetWMNormalHints(display.as_ptr(), window, size_hints);
            x_free(size_hints);

            // We will use redraw the buffer when we are exposed.
            xlib::XSelectInput(
                display.as_ptr(),
                window,
                (xlib::ExposureMask
                    | xlib::KeyPressMask
                    | xlib::KeyReleaseMask
                    | xlib::ButtonPressMask
                    | xlib::ButtonReleaseMask
                    | xlib::PointerMotionMask) as i64,
            );

            xlib::XClearWindow(display.as_ptr(), window);
            xlib::XMapRaised(display.as_ptr(), window);

            // Flush everything so that the window is visible immediately.
            display.flush();

            Ok(Surface {
                display,
                visual,
                depth,
                window,
                gc,
                width,
                height,
                event_devices: Default::default(),
                keycode_translator,
                buffers: Default::default(),
                buffer_next: 0,
                buffer_completion_type,
                delete_window_atom,
                close_requested: false,
            })
        }
    }

    /// Returns index of the current (on-screen) buffer, or 0 if there are no buffers.
    fn current_buffer(&self) -> usize {
        match self.buffer_next.checked_sub(1) {
            Some(i) => i,
            None => self.buffers.len() - 1,
        }
    }

    fn dispatch_to_event_devices(
        &mut self,
        events: &[virtio_input_event],
        device_type: EventDeviceKind,
    ) {
        for event_device in self.event_devices.values_mut() {
            if event_device.kind() != device_type {
                continue;
            }
            if let Err(e) = event_device.send_report(events.iter().cloned()) {
                error!("error sending events to event device: {}", e);
            }
        }
    }

    fn handle_event(&mut self, ev: XEvent) {
        match ev.as_enum(self.buffer_completion_type) {
            XEventEnum::KeyEvent(key) => {
                if let Some(linux_keycode) = self.keycode_translator.translate(key.keycode) {
                    let events = &[virtio_input_event::key(
                        linux_keycode,
                        key.type_ == xlib::KeyPress as i32,
                    )];
                    self.dispatch_to_event_devices(events, EventDeviceKind::Keyboard);
                }
            }
            XEventEnum::ButtonEvent {
                event: button_event,
                pressed,
            } => {
                // We only support a single touch from button 1 (left mouse button).
                if button_event.button & xlib::Button1 != 0 {
                    // The touch event *must* be first per the Linux input subsystem's guidance.
                    let events = &[
                        virtio_input_event::touch(pressed),
                        virtio_input_event::absolute_x(max(0, button_event.x) as u32),
                        virtio_input_event::absolute_y(max(0, button_event.y) as u32),
                    ];
                    self.dispatch_to_event_devices(events, EventDeviceKind::Touchscreen);
                }
            }
            XEventEnum::Motion(motion) => {
                if motion.state & xlib::Button1Mask != 0 {
                    let events = &[
                        virtio_input_event::touch(true),
                        virtio_input_event::absolute_x(max(0, motion.x) as u32),
                        virtio_input_event::absolute_y(max(0, motion.y) as u32),
                    ];
                    self.dispatch_to_event_devices(events, EventDeviceKind::Touchscreen);
                }
            }
            XEventEnum::Expose => self.draw_buffer(self.current_buffer()),
            XEventEnum::ClientMessage(xclient_data) => {
                if xclient_data == self.delete_window_atom {
                    self.close_requested = true;
                }
            }
            XEventEnum::ShmCompletionEvent(shmseg) => {
                // Find the buffer associated with this event and mark it as not in use.
                for buffer_opt in self.buffers.iter_mut() {
                    if let Some(buffer) = buffer_opt {
                        if buffer.segment_info.shmseg == shmseg {
                            buffer.in_use = false;
                        }
                    }
                }
            }
            XEventEnum::Unhandled => {}
        }
    }

    /// Draws the indicated buffer onto the screen.
    fn draw_buffer(&mut self, buffer_index: usize) {
        let buffer = match self.buffers.get_mut(buffer_index) {
            Some(Some(b)) => b,
            _ => {
                // If there is no buffer, that means the framebuffer was never set and we should
                // simply blank the window with arbitrary contents.
                unsafe {
                    xlib::XClearWindow(self.display.as_ptr(), self.window);
                }
                return;
            }
        };
        // Mark the buffer as in use. When the XShmCompletionEvent occurs, this will get marked
        // false.
        buffer.in_use = true;
        unsafe {
            xlib::XShmPutImage(
                self.display.as_ptr(),
                self.window,
                self.gc,
                buffer.image,
                0, // src x
                0, // src y
                0, // dst x
                0, // dst y
                self.width,
                self.height,
                true as i32, /* send XShmCompletionEvent event */
            );
            self.display.flush();
        }
    }

    /// Gets the buffer at buffer_index, allocating it if necessary.
    fn lazily_allocate_buffer(&mut self, buffer_index: usize) -> Option<&Buffer> {
        if buffer_index >= self.buffers.len() {
            return None;
        }

        if self.buffers[buffer_index].is_some() {
            return self.buffers[buffer_index].as_ref();
        }
        // The buffer_index is valid and the buffer was never created, so we create it now.
        unsafe {
            // The docs for XShmCreateImage imply that XShmSegmentInfo must be allocated to live at
            // least as long as the XImage, which probably means it can't move either. Use a Box in
            // order to fulfill those requirements.
            let mut segment_info: Box<xlib::XShmSegmentInfo> = Box::new(zeroed());
            let image = xlib::XShmCreateImage(
                self.display.as_ptr(),
                self.visual,
                self.depth,
                xlib::ZPixmap as i32,
                null_mut(),
                segment_info.as_mut(),
                self.width,
                self.height,
            );
            if image.is_null() {
                return None;
            }
            let size = (*image)
                .bytes_per_line
                .checked_mul((*image).height)
                .unwrap();
            segment_info.shmid = shmget(IPC_PRIVATE, size as usize, IPC_CREAT | 0o777);
            if segment_info.shmid == -1 {
                xlib::XDestroyImage(image);
                return None;
            }
            segment_info.shmaddr = shmat(segment_info.shmid, null_mut(), 0) as *mut _;
            if segment_info.shmaddr == (-1isize) as *mut _ {
                xlib::XDestroyImage(image);
                shmctl(segment_info.shmid, IPC_RMID, null_mut());
                return None;
            }
            (*image).data = segment_info.shmaddr;
            segment_info.readOnly = true as i32;
            xlib::XShmAttach(self.display.as_ptr(), segment_info.as_mut());
            self.buffers[buffer_index] = Some(Buffer {
                display: self.display.clone(),
                image,
                segment_info,
                size: size as usize,
                in_use: false,
            });
            self.buffers[buffer_index].as_ref()
        }
    }

    /// Gets the next framebuffer, allocating if necessary.
    fn framebuffer(&mut self) -> Option<GpuDisplayFramebuffer> {
        // Framebuffers are lazily allocated. If the next buffer is not in self.buffers, add it
        // using push_new_buffer and then get its memory.
        let framebuffer = self.lazily_allocate_buffer(self.buffer_next)?;
        let bytes_per_pixel = framebuffer.bytes_per_pixel() as u32;
        Some(GpuDisplayFramebuffer::new(
            framebuffer.as_volatile_slice(),
            framebuffer.stride() as u32,
            bytes_per_pixel,
        ))
    }

    /// True if the next buffer is in use because of an XShmPutImage call.
    fn next_buffer_in_use(&self) -> bool {
        // Buffers that have not yet been made are not in use, hence unwrap_or(false).
        self.buffers
            .get(self.buffer_next)
            .and_then(|b| Some(b.as_ref()?.in_use))
            .unwrap_or(false)
    }

    /// Puts the next buffer onto the screen and sets the next buffer in the swap chain.
    fn flip(&mut self) {
        let current_buffer_index = self.buffer_next;
        self.buffer_next = (self.buffer_next + 1) % self.buffers.len();
        self.draw_buffer(current_buffer_index);
    }
}

impl Drop for Surface {
    fn drop(&mut self) {
        // Safe given it should always be of the correct type.
        unsafe {
            xlib::XFreeGC(self.display.as_ptr(), self.gc);
            xlib::XDestroyWindow(self.display.as_ptr(), self.window);
        }
    }
}

#[derive(PollToken)]
enum DisplayXPollToken {
    Display,
    EventDevice { event_device_id: u32 },
}

pub struct DisplayX {
    poll_ctx: PollContext<DisplayXPollToken>,
    display: XDisplay,
    screen: XScreen,
    visual: *mut xlib::Visual,
    next_id: ObjectId,
    surfaces: BTreeMap<ObjectId, Surface>,
    event_devices: BTreeMap<ObjectId, EventDevice>,
}

impl DisplayX {
    pub fn open_display(display: Option<&str>) -> Result<DisplayX, GpuDisplayError> {
        let poll_ctx = PollContext::new().map_err(|_| GpuDisplayError::Allocate)?;

        let display_cstr = match display.map(CString::new) {
            Some(Ok(s)) => Some(s),
            Some(Err(_)) => return Err(GpuDisplayError::InvalidPath),
            None => None,
        };

        unsafe {
            // Open the display
            let display = match NonNull::new(xlib::XOpenDisplay(
                display_cstr
                    .as_ref()
                    .map(|s| CStr::as_ptr(s))
                    .unwrap_or(null()),
            )) {
                Some(display_ptr) => XDisplay(Rc::new(display_ptr)),
                None => return Err(GpuDisplayError::Connect),
            };

            poll_ctx
                .add(&display, DisplayXPollToken::Display)
                .map_err(|_| GpuDisplayError::Allocate)?;

            // Check for required extension.
            if !display.supports_shm() {
                return Err(GpuDisplayError::RequiredFeature("xshm extension"));
            }

            let screen = display
                .default_screen()
                .ok_or(GpuDisplayError::Connect)
                .unwrap();
            let screen_number = screen.get_number();

            // Check for and save required visual (24-bit BGR for the default screen).
            let mut visual_info_template = xlib::XVisualInfo {
                visual: null_mut(),
                visualid: 0,
                screen: screen_number,
                depth: 24,
                class: 0,
                red_mask: 0x00ff0000,
                green_mask: 0x0000ff00,
                blue_mask: 0x000000ff,
                colormap_size: 0,
                bits_per_rgb: 0,
            };
            let visual_info = xlib::XGetVisualInfo(
                display.as_ptr(),
                (xlib::VisualScreenMask
                    | xlib::VisualDepthMask
                    | xlib::VisualRedMaskMask
                    | xlib::VisualGreenMaskMask
                    | xlib::VisualBlueMaskMask) as i64,
                &mut visual_info_template,
                &mut 0,
            );
            if visual_info.is_null() {
                return Err(GpuDisplayError::RequiredFeature("no matching visual"));
            }
            let visual = (*visual_info).visual;
            x_free(visual_info);

            Ok(DisplayX {
                poll_ctx,
                display,
                screen,
                visual,
                next_id: ObjectId::new(1).unwrap(),
                surfaces: Default::default(),
                event_devices: Default::default(),
            })
        }
    }

    fn surface_ref(&self, surface_id: u32) -> Option<&Surface> {
        ObjectId::new(surface_id).and_then(move |id| self.surfaces.get(&id))
    }

    fn surface_mut(&mut self, surface_id: u32) -> Option<&mut Surface> {
        ObjectId::new(surface_id).and_then(move |id| self.surfaces.get_mut(&id))
    }

    fn event_device(&self, event_device_id: u32) -> Option<&EventDevice> {
        ObjectId::new(event_device_id).and_then(move |id| self.event_devices.get(&id))
    }

    fn event_device_mut(&mut self, event_device_id: u32) -> Option<&mut EventDevice> {
        ObjectId::new(event_device_id).and_then(move |id| self.event_devices.get_mut(&id))
    }

    fn handle_event(&mut self, ev: XEvent) {
        let window = ev.window();
        for surface in self.surfaces.values_mut() {
            if surface.window != window {
                continue;
            }
            surface.handle_event(ev);
            return;
        }
    }

    fn dispatch_display_events(&mut self) {
        loop {
            self.display.flush();
            if !self.display.pending_events() {
                break;
            }
            let ev = self.display.next_event();
            self.handle_event(ev);
        }
    }

    fn handle_event_device(&mut self, event_device_id: u32) {
        if let Some(event_device) = self.event_device(event_device_id) {
            // TODO(zachr): decode the event and forward to the device.
            let _ = event_device.recv_event_encoded();
        }
    }

    fn handle_poll_ctx(&mut self) -> sys_util::Result<()> {
        let poll_events = self.poll_ctx.wait_timeout(Duration::default())?.to_owned();
        for poll_event in poll_events.as_ref().iter_writable() {
            if let DisplayXPollToken::EventDevice { event_device_id } = poll_event.token() {
                if let Some(event_device) = self.event_device_mut(event_device_id) {
                    if !event_device.flush_buffered_events()? {
                        continue;
                    }
                }
                // Although this looks exactly like the previous if-block, we need to reborrow self
                // as immutable in order to make use of self.poll_ctx.
                if let Some(event_device) = self.event_device(event_device_id) {
                    self.poll_ctx.modify(
                        event_device,
                        WatchingEvents::empty().set_read(),
                        DisplayXPollToken::EventDevice { event_device_id },
                    )?;
                }
            }
        }

        for poll_event in poll_events.as_ref().iter_readable() {
            match poll_event.token() {
                DisplayXPollToken::Display => self.dispatch_display_events(),
                DisplayXPollToken::EventDevice { event_device_id } => {
                    self.handle_event_device(event_device_id)
                }
            }
        }

        Ok(())
    }
}

impl DisplayT for DisplayX {
    fn dispatch_events(&mut self) {
        if let Err(e) = self.handle_poll_ctx() {
            error!("failed to dispatch events: {}", e);
        }
    }

    fn create_surface(
        &mut self,
        parent_surface_id: Option<u32>,
        width: u32,
        height: u32,
    ) -> Result<u32, GpuDisplayError> {
        if parent_surface_id.is_some() {
            return Err(GpuDisplayError::Unsupported);
        }

        let new_surface = Surface::create(
            self.display.clone(),
            &self.screen,
            self.visual,
            width,
            height,
        )?;
        let new_surface_id = self.next_id;
        self.surfaces.insert(new_surface_id, new_surface);
        self.next_id = ObjectId::new(self.next_id.get() + 1).unwrap();

        Ok(new_surface_id.get())
    }

    fn release_surface(&mut self, surface_id: u32) {
        if let Some(mut surface) =
            ObjectId::new(surface_id).and_then(|id| self.surfaces.remove(&id))
        {
            self.event_devices.append(&mut surface.event_devices);
        }
    }

    fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer> {
        self.surface_mut(surface_id).and_then(|s| s.framebuffer())
    }

    fn next_buffer_in_use(&self, surface_id: u32) -> bool {
        self.surface_ref(surface_id)
            .map(|s| s.next_buffer_in_use())
            .unwrap_or(false)
    }

    fn flip(&mut self, surface_id: u32) {
        if let Some(surface) = self.surface_mut(surface_id) {
            surface.flip()
        }
    }

    fn close_requested(&self, surface_id: u32) -> bool {
        self.surface_ref(surface_id)
            .map(|s| s.close_requested)
            .unwrap_or(true)
    }

    #[allow(unused_variables)]
    fn import_dmabuf(
        &mut self,
        fd: RawFd,
        offset: u32,
        stride: u32,
        modifiers: u64,
        width: u32,
        height: u32,
        fourcc: u32,
    ) -> Result<u32, GpuDisplayError> {
        Err(GpuDisplayError::Unsupported)
    }
    #[allow(unused_variables)]
    fn release_import(&mut self, import_id: u32) {
        // unsupported
    }
    #[allow(unused_variables)]
    fn commit(&mut self, surface_id: u32) {
        // unsupported
    }
    #[allow(unused_variables)]
    fn flip_to(&mut self, surface_id: u32, import_id: u32) {
        // unsupported
    }
    #[allow(unused_variables)]
    fn set_position(&mut self, surface_id: u32, x: u32, y: u32) {
        // unsupported
    }

    fn import_event_device(&mut self, event_device: EventDevice) -> Result<u32, GpuDisplayError> {
        let new_event_device_id = self.next_id;

        self.poll_ctx
            .add(
                &event_device,
                DisplayXPollToken::EventDevice {
                    event_device_id: new_event_device_id.get(),
                },
            )
            .map_err(|_| GpuDisplayError::Allocate)?;

        self.event_devices.insert(new_event_device_id, event_device);
        self.next_id = ObjectId::new(self.next_id.get() + 1).unwrap();

        Ok(new_event_device_id.get())
    }

    fn release_event_device(&mut self, event_device_id: u32) {
        ObjectId::new(event_device_id).and_then(|id| self.event_devices.remove(&id));
    }

    fn attach_event_device(&mut self, surface_id: u32, event_device_id: u32) {
        let event_device_id = match ObjectId::new(event_device_id) {
            Some(id) => id,
            None => return,
        };
        let surface_id = match ObjectId::new(surface_id) {
            Some(id) => id,
            None => return,
        };
        let surface = self.surfaces.get_mut(&surface_id).unwrap();
        let event_device = self.event_devices.remove(&event_device_id).unwrap();
        surface.event_devices.insert(event_device_id, event_device);
    }
}

impl AsRawFd for DisplayX {
    fn as_raw_fd(&self) -> RawFd {
        self.poll_ctx.as_raw_fd()
    }
}