gpu_display: add X11 input bindings.

Adds bindings to the X11 display window to capture keyboard & mouse
input & send it to the guest via an EventDevice.

Original implementation by zachr@chromium.org.

BUG=chromium:1023975
TEST=None

Change-Id: I33156a8ca0b8c610a2080e3b6891cca2a865734b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1971121
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Noah Gold <nkgold@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
diff --git a/Cargo.lock b/Cargo.lock
index 9536949..8dc6ff4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -153,6 +153,7 @@
  "kvm 0.1.0",
  "kvm_sys 0.1.0",
  "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "linux_input_sys 0.1.0",
  "msg_on_socket_derive 0.1.0",
  "msg_socket 0.1.0",
  "net_sys 0.1.0",
@@ -218,6 +219,7 @@
  "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
  "data_model 0.1.0",
  "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "linux_input_sys 0.1.0",
  "sys_util 0.1.0",
 ]
 
@@ -290,6 +292,15 @@
 ]
 
 [[package]]
+name = "linux_input_sys"
+version = "0.1.0"
+dependencies = [
+ "data_model 0.1.0",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
+]
+
+[[package]]
 name = "log"
 version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/devices/Cargo.toml b/devices/Cargo.toml
index 7ea90b5..236cf4e 100644
--- a/devices/Cargo.toml
+++ b/devices/Cargo.toml
@@ -24,6 +24,7 @@
 kvm = { path = "../kvm" }
 kvm_sys = { path = "../kvm_sys" }
 libc = "*"
+linux_input_sys = { path = "../linux_input_sys" }
 msg_on_socket_derive = { path = "../msg_socket/msg_on_socket_derive" }
 msg_socket = { path = "../msg_socket" }
 net_sys = { path = "../net_sys" }
diff --git a/devices/src/virtio/input/event_source.rs b/devices/src/virtio/input/event_source.rs
index 7ca7e13..d190e18 100644
--- a/devices/src/virtio/input/event_source.rs
+++ b/devices/src/virtio/input/event_source.rs
@@ -8,6 +8,7 @@
 use super::InputError;
 use super::Result;
 use data_model::DataInit;
+use linux_input_sys::input_event;
 use std::collections::VecDeque;
 use std::io::Read;
 use std::io::Write;
@@ -15,20 +16,11 @@
 use std::os::unix::io::{AsRawFd, RawFd};
 use sys_util::{error, warn};
 
-#[derive(Copy, Clone, Debug, Default)]
-#[repr(C)]
-pub struct input_event {
-    timestamp_fields: [u64; 2],
-    pub type_: u16,
-    pub code: u16,
-    pub value: u32,
+trait ConvertFromVirtioInputEvent {
+    fn from_virtio_input_event(other: &virtio_input_event) -> input_event;
 }
-// Safe because it only has data and has no implicit padding.
-unsafe impl DataInit for input_event {}
 
-impl input_event {
-    const EVENT_SIZE: usize = size_of::<input_event>();
-
+impl ConvertFromVirtioInputEvent for input_event {
     fn from_virtio_input_event(other: &virtio_input_event) -> input_event {
         input_event {
             timestamp_fields: [0, 0],
diff --git a/devices/src/virtio/input/mod.rs b/devices/src/virtio/input/mod.rs
index 2490c52..fb05240 100644
--- a/devices/src/virtio/input/mod.rs
+++ b/devices/src/virtio/input/mod.rs
@@ -15,11 +15,12 @@
 use data_model::{DataInit, Le16, Le32};
 use sys_util::{error, warn, EventFd, GuestMemory, PollContext, PollToken};
 
-use self::event_source::{input_event, EvdevEventSource, EventSource, SocketEventSource};
+use self::event_source::{EvdevEventSource, EventSource, SocketEventSource};
 use super::{
     copy_config, DescriptorChain, DescriptorError, Interrupt, Queue, Reader, VirtioDevice, Writer,
     TYPE_INPUT,
 };
+use linux_input_sys::input_event;
 use std::collections::BTreeMap;
 use std::fmt::{self, Display};
 use std::io::Read;
diff --git a/gpu_display/Cargo.toml b/gpu_display/Cargo.toml
index 54f2bad..21ee9ca 100644
--- a/gpu_display/Cargo.toml
+++ b/gpu_display/Cargo.toml
@@ -11,6 +11,7 @@
 data_model = { path = "../data_model" }
 libc = "*"
 sys_util = { path = "../sys_util" }
+linux_input_sys = { path = "../linux_input_sys" }
 
 [build-dependencies]
 cc = "=1.0.25"
diff --git a/gpu_display/src/event_device.rs b/gpu_display/src/event_device.rs
index 673a104..5f1bbc7 100644
--- a/gpu_display/src/event_device.rs
+++ b/gpu_display/src/event_device.rs
@@ -2,25 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use data_model::DataInit;
+use linux_input_sys::input_event;
 use std::collections::VecDeque;
-use std::io::{self, Read, Write};
+use std::io::{self, Error, ErrorKind, Read, Write};
 use std::iter::ExactSizeIterator;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::os::unix::net::UnixStream;
 
-const EVENT_SIZE: usize = 4;
+const EVENT_SIZE: usize = input_event::EVENT_SIZE;
 const EVENT_BUFFER_LEN_MAX: usize = 16 * EVENT_SIZE;
 
-const EV_SYN: u16 = 0x00;
-const EV_KEY: u16 = 0x01;
-const EV_REL: u16 = 0x02;
-const EV_ABS: u16 = 0x03;
-const SYN_REPORT: u16 = 0;
-const REL_X: u16 = 0x00;
-const REL_Y: u16 = 0x01;
-const ABS_X: u16 = 0x00;
-const ABS_Y: u16 = 0x01;
-
 // /// Half-way build `EventDevice` with only the `event_socket` defined. Finish building the
 // /// `EventDevice` by using `status_socket`.
 // pub struct PartialEventDevice(UnixStream);
@@ -45,69 +37,6 @@
     Keyboard,
 }
 
-#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
-pub struct EventEncoded {
-    pub type_: u16,
-    pub code: u16,
-    pub value: u32,
-}
-
-impl EventEncoded {
-    #[inline]
-    pub fn syn() -> EventEncoded {
-        EventEncoded {
-            type_: EV_SYN,
-            code: SYN_REPORT,
-            value: 0,
-        }
-    }
-
-    #[inline]
-    pub fn absolute(code: u16, value: u32) -> EventEncoded {
-        EventEncoded {
-            type_: EV_ABS,
-            code,
-            value,
-        }
-    }
-
-    #[inline]
-    pub fn absolute_x(x: u32) -> EventEncoded {
-        Self::absolute(ABS_X, x)
-    }
-
-    #[inline]
-    pub fn absolute_y(y: u32) -> EventEncoded {
-        Self::absolute(ABS_Y, y)
-    }
-
-    #[inline]
-    pub fn key(code: u16, pressed: bool) -> EventEncoded {
-        EventEncoded {
-            type_: EV_KEY,
-            code,
-            value: if pressed { 1 } else { 0 },
-        }
-    }
-
-    #[inline]
-    pub fn from_bytes(v: [u8; 8]) -> EventEncoded {
-        EventEncoded {
-            type_: u16::from_le_bytes([v[0], v[1]]),
-            code: u16::from_le_bytes([v[2], v[3]]),
-            value: u32::from_le_bytes([v[4], v[5], v[6], v[7]]),
-        }
-    }
-
-    #[inline]
-    pub fn to_bytes(&self) -> [u8; 8] {
-        let a = self.type_.to_le_bytes();
-        let b = self.code.to_le_bytes();
-        let c = self.value.to_le_bytes();
-        [a[0], a[1], b[0], b[1], c[0], c[1], c[2], c[3]]
-    }
-}
-
 /// Encapsulates a virtual event device, such as a mouse or keyboard
 pub struct EventDevice {
     kind: EventDeviceKind,
@@ -164,7 +93,7 @@
         self.event_buffer.is_empty()
     }
 
-    pub fn send_report<E: IntoIterator<Item = EventEncoded>>(
+    pub fn send_report<E: IntoIterator<Item = input_event>>(
         &mut self,
         events: E,
     ) -> io::Result<bool>
@@ -177,24 +106,24 @@
         }
 
         for event in it {
-            let bytes = event.to_bytes();
+            let bytes = event.as_slice();
             self.event_buffer.extend(bytes.iter());
         }
 
         self.event_buffer
-            .extend(EventEncoded::syn().to_bytes().iter());
+            .extend(input_event::syn().as_slice().iter());
 
         self.flush_buffered_events()
     }
 
     /// Sends the given `event`, returning `Ok(true)` if, after this function returns, there are no
     /// buffered events remaining.
-    pub fn send_event_encoded(&mut self, event: EventEncoded) -> io::Result<bool> {
+    pub fn send_event_encoded(&mut self, event: input_event) -> io::Result<bool> {
         if !self.flush_buffered_events()? {
             return Ok(false);
         }
 
-        let bytes = event.to_bytes();
+        let bytes = event.as_slice();
         let written = self.event_socket.write(&bytes)?;
 
         if written == bytes.len() {
@@ -208,10 +137,16 @@
         Ok(false)
     }
 
-    pub fn recv_event_encoded(&self) -> io::Result<EventEncoded> {
-        let mut event_bytes = [0; 8];
+    pub fn recv_event_encoded(&self) -> io::Result<input_event> {
+        let mut event_bytes = [0u8; 24];
         (&self.event_socket).read_exact(&mut event_bytes)?;
-        Ok(EventEncoded::from_bytes(event_bytes))
+        match input_event::from_slice(&event_bytes) {
+            Some(event) => Ok(*event),
+            None => Err(Error::new(
+                ErrorKind::InvalidInput,
+                "failed to read input_event",
+            )),
+        }
     }
 }
 
diff --git a/gpu_display/src/generated/xlib.rs b/gpu_display/src/generated/xlib.rs
index 1dc4c9b..f6ff382 100644
--- a/gpu_display/src/generated/xlib.rs
+++ b/gpu_display/src/generated/xlib.rs
@@ -20,9 +20,13 @@
 pub const ExposureMask: u32 = 32768;
 pub const KeyPress: u32 = 2;
 pub const KeyRelease: u32 = 3;
+pub const ButtonPress: u32 = 4;
+pub const ButtonRelease: u32 = 5;
 pub const MotionNotify: u32 = 6;
 pub const Expose: u32 = 12;
 pub const ClientMessage: u32 = 33;
+pub const Button1Mask: u32 = 256;
+pub const Button1: u32 = 1;
 pub const ZPixmap: u32 = 2;
 pub const XK_VoidSymbol: u32 = 16777215;
 pub const XK_BackSpace: u32 = 65288;
diff --git a/gpu_display/src/generated/xlib_generator.sh b/gpu_display/src/generated/xlib_generator.sh
index e64d662..304904a 100755
--- a/gpu_display/src/generated/xlib_generator.sh
+++ b/gpu_display/src/generated/xlib_generator.sh
@@ -55,7 +55,11 @@
   --whitelist-function XShmPutImage \
   --whitelist-function XShmQueryExtension \
   --whitelist-var 'XK_.*' \
+  --whitelist-var ButtonPress \
   --whitelist-var ButtonPressMask \
+  --whitelist-var Button1 \
+  --whitelist-var Button1Mask \
+  --whitelist-var ButtonRelease \
   --whitelist-var ButtonReleaseMask \
   --whitelist-var ClientMessage \
   --whitelist-var Expose \
diff --git a/gpu_display/src/gpu_display_x.rs b/gpu_display/src/gpu_display_x.rs
index 1df7fc2..c0074ca 100644
--- a/gpu_display/src/gpu_display_x.rs
+++ b/gpu_display/src/gpu_display_x.rs
@@ -11,6 +11,8 @@
 )]
 mod xlib;
 
+use linux_input_sys::input_event;
+use std::cmp::max;
 use std::collections::BTreeMap;
 use std::ffi::{c_void, CStr, CString};
 use std::mem::{transmute_copy, zeroed};
@@ -18,17 +20,22 @@
 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::rc::{Rc, Weak};
+use std::time::Duration;
 
 use libc::{shmat, shmctl, shmdt, shmget, IPC_CREAT, IPC_PRIVATE, IPC_RMID};
 
-use crate::{DisplayT, EventDevice, GpuDisplayError, GpuDisplayFramebuffer};
+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 SurfaceId = NonZeroU32;
+type ObjectId = NonZeroU32;
 
 /// A wrapper for XFree that takes any type.
 unsafe fn x_free<T>(t: *mut T) {
@@ -119,6 +126,16 @@
     // 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 })
@@ -136,6 +153,12 @@
 }
 
 enum XEventEnum {
+    KeyEvent(xlib::XKeyEvent),
+    ButtonEvent {
+        event: xlib::XButtonEvent,
+        pressed: bool,
+    },
+    Motion(xlib::XMotionEvent),
     Expose,
     ClientMessage(u64),
     ShmCompletionEvent(xlib::ShmSeg),
@@ -201,6 +224,8 @@
     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],
@@ -220,6 +245,7 @@
         width: u32,
         height: u32,
     ) -> Result<Surface, GpuDisplayError> {
+        let keycode_translator = KeycodeTranslator::new(KeycodeTypes::XkbScancode);
         unsafe {
             let depth = xlib::XDefaultDepthOfScreen(screen.as_ptr()) as u32;
 
@@ -263,7 +289,16 @@
             x_free(size_hints);
 
             // We will use redraw the buffer when we are exposed.
-            xlib::XSelectInput(display.as_ptr(), window, xlib::ExposureMask as i64);
+            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);
@@ -279,6 +314,8 @@
                 gc,
                 width,
                 height,
+                event_devices: Default::default(),
+                keycode_translator,
                 buffers: Default::default(),
                 buffer_next: 0,
                 buffer_completion_type,
@@ -296,8 +333,53 @@
         }
     }
 
+    fn dispatch_to_event_devices(&mut self, events: &[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 = &[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 = &[
+                        input_event::touch(pressed),
+                        input_event::absolute_x(max(0, button_event.x) as u32),
+                        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 = &[
+                        input_event::touch(true),
+                        input_event::absolute_x(max(0, motion.x) as u32),
+                        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 {
@@ -449,16 +531,26 @@
     }
 }
 
+#[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_surface_id: SurfaceId,
-    surfaces: BTreeMap<SurfaceId, Surface>,
+    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),
@@ -477,6 +569,10 @@
                 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"));
@@ -518,21 +614,31 @@
             x_free(visual_info);
 
             Ok(DisplayX {
+                poll_ctx,
                 display,
                 screen,
                 visual,
-                next_surface_id: SurfaceId::new(1).unwrap(),
+                next_id: ObjectId::new(1).unwrap(),
                 surfaces: Default::default(),
+                event_devices: Default::default(),
             })
         }
     }
 
     fn surface_ref(&self, surface_id: u32) -> Option<&Surface> {
-        SurfaceId::new(surface_id).and_then(move |id| self.surfaces.get(&id))
+        ObjectId::new(surface_id).and_then(move |id| self.surfaces.get(&id))
     }
 
     fn surface_mut(&mut self, surface_id: u32) -> Option<&mut Surface> {
-        SurfaceId::new(surface_id).and_then(move |id| self.surfaces.get_mut(&id))
+        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) {
@@ -545,10 +651,8 @@
             return;
         }
     }
-}
 
-impl DisplayT for DisplayX {
-    fn dispatch_events(&mut self) {
+    fn dispatch_display_events(&mut self) {
         loop {
             self.display.flush();
             if !self.display.pending_events() {
@@ -559,6 +663,54 @@
         }
     }
 
+    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>,
@@ -576,15 +728,19 @@
             width,
             height,
         )?;
-        let new_surface_id = self.next_surface_id;
+        let new_surface_id = self.next_id;
         self.surfaces.insert(new_surface_id, new_surface);
-        self.next_surface_id = SurfaceId::new(self.next_surface_id.get() + 1).unwrap();
+        self.next_id = ObjectId::new(self.next_id.get() + 1).unwrap();
 
         Ok(new_surface_id.get())
     }
 
     fn release_surface(&mut self, surface_id: u32) {
-        SurfaceId::new(surface_id).and_then(|id| self.surfaces.remove(&id));
+        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> {
@@ -638,19 +794,46 @@
     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> {
-        Err(GpuDisplayError::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) {
-        // unsupported
+
+    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) {
-        // unsupported
+        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.display.as_raw_fd()
+        self.poll_ctx.as_raw_fd()
     }
 }
diff --git a/gpu_display/src/lib.rs b/gpu_display/src/lib.rs
index 8985405..1d9fcdc 100644
--- a/gpu_display/src/lib.rs
+++ b/gpu_display/src/lib.rs
@@ -17,7 +17,7 @@
 mod gpu_display_x;
 mod keycode_converter;
 
-pub use event_device::{EventDevice, EventDeviceKind, EventEncoded};
+pub use event_device::{EventDevice, EventDeviceKind};
 
 /// An error generated by `GpuDisplay`.
 #[derive(Debug)]