summary refs log tree commit diff
path: root/gpu_display
diff options
context:
space:
mode:
authorZach Reizner <zachr@google.com>2019-04-16 15:09:20 -0700
committerCommit Bot <commit-bot@chromium.org>2019-07-25 22:15:48 +0000
commitf5285c647acacb4f25ef8cf9334254b976e71686 (patch)
treefcc238ec97736727a9c18b3b9de29be3dce3983e /gpu_display
parentb2110bef59d72529d99c722df9b3e9a1d705e6f4 (diff)
downloadcrosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.gz
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.bz2
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.lz
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.xz
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.zst
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.zip
gpu_display: add X11 backend
This change adds an X11 backend to the gpu_display crate. With this
addition, the virtio-gpu device can display to traditional linux
desktops that only have X11 output.

Change-Id: I86c80cac91ca5bdc97588194a44040273ae69385
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1591572
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
Commit-Queue: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Auto-Submit: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'gpu_display')
-rw-r--r--gpu_display/Cargo.toml3
-rw-r--r--gpu_display/examples/simple.rs4
-rw-r--r--gpu_display/examples/simple_open.rs25
-rw-r--r--gpu_display/src/generated/xlib.rs892
-rwxr-xr-xgpu_display/src/generated/xlib_generator.sh76
-rw-r--r--gpu_display/src/generated/xlib_wrapper.h4
-rw-r--r--gpu_display/src/gpu_display_wl.rs347
-rw-r--r--gpu_display/src/gpu_display_x.rs647
-rw-r--r--gpu_display/src/lib.rs404
9 files changed, 2148 insertions, 254 deletions
diff --git a/gpu_display/Cargo.toml b/gpu_display/Cargo.toml
index 0177f1e..54f2bad 100644
--- a/gpu_display/Cargo.toml
+++ b/gpu_display/Cargo.toml
@@ -4,6 +4,9 @@ version = "0.1.0"
 authors = ["The Chromium OS Authors"]
 edition = "2018"
 
+[features]
+x = []
+
 [dependencies]
 data_model = { path = "../data_model" }
 libc = "*"
diff --git a/gpu_display/examples/simple.rs b/gpu_display/examples/simple.rs
index 140e6cc..ace9828 100644
--- a/gpu_display/examples/simple.rs
+++ b/gpu_display/examples/simple.rs
@@ -5,8 +5,10 @@
 use gpu_display::*;
 
 fn main() {
-    let mut disp = GpuDisplay::new("/run/wayland-0").unwrap();
+    let mut disp = GpuDisplay::open_wayland(None::<&str>).unwrap();
     let surface_id = disp.create_surface(None, 1280, 1024).unwrap();
+    disp.flip(surface_id);
+    disp.commit(surface_id);
     while !disp.close_requested(surface_id) {
         disp.dispatch_events();
     }
diff --git a/gpu_display/examples/simple_open.rs b/gpu_display/examples/simple_open.rs
new file mode 100644
index 0000000..bb7b1a2
--- /dev/null
+++ b/gpu_display/examples/simple_open.rs
@@ -0,0 +1,25 @@
+use gpu_display::GpuDisplay;
+
+fn main() {
+    let mut disp = GpuDisplay::open_x(None::<&str>).unwrap();
+    let surface_id = disp.create_surface(None, 1280, 1024).unwrap();
+
+    let mem = disp.framebuffer(surface_id).unwrap();
+    for y in 0..1024 {
+        let mut row = [0u32; 1280];
+        for x in 0..1280 {
+            let b = ((x as f32 / 1280.0) * 256.0) as u32;
+            let g = ((y as f32 / 1024.0) * 256.0) as u32;
+            row[x] = b | (g << 8);
+        }
+        mem.as_volatile_slice()
+            .offset((1280 * 4 * y) as u64)
+            .unwrap()
+            .copy_from(&row);
+    }
+    disp.flip(surface_id);
+
+    while !disp.close_requested(surface_id) {
+        disp.dispatch_events();
+    }
+}
diff --git a/gpu_display/src/generated/xlib.rs b/gpu_display/src/generated/xlib.rs
new file mode 100644
index 0000000..bbfa6bd
--- /dev/null
+++ b/gpu_display/src/generated/xlib.rs
@@ -0,0 +1,892 @@
+// 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.
+
+//! Generated using ./xlib_generator.sh
+
+#[link(name = "X11")]
+extern "C" {}
+
+#[link(name = "Xext")]
+extern "C" {}
+
+/* automatically generated by rust-bindgen */
+
+pub const ExposureMask: u32 = 32768;
+pub const Expose: u32 = 12;
+pub const ClientMessage: u32 = 33;
+pub const ZPixmap: u32 = 2;
+pub const PMinSize: u32 = 16;
+pub const PMaxSize: u32 = 32;
+pub const VisualScreenMask: u32 = 2;
+pub const VisualDepthMask: u32 = 4;
+pub const VisualRedMaskMask: u32 = 16;
+pub const VisualGreenMaskMask: u32 = 32;
+pub const VisualBlueMaskMask: u32 = 64;
+pub const ShmCompletion: u32 = 0;
+pub type XID = ::std::os::raw::c_ulong;
+pub type Atom = ::std::os::raw::c_ulong;
+pub type VisualID = ::std::os::raw::c_ulong;
+pub type Time = ::std::os::raw::c_ulong;
+pub type Window = XID;
+pub type Drawable = XID;
+pub type Font = XID;
+pub type Pixmap = XID;
+pub type Colormap = XID;
+pub type XPointer = *mut ::std::os::raw::c_char;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct _XExtData {
+    pub number: ::std::os::raw::c_int,
+    pub next: *mut _XExtData,
+    pub free_private: ::std::option::Option<
+        unsafe extern "C" fn(extension: *mut _XExtData) -> ::std::os::raw::c_int,
+    >,
+    pub private_data: XPointer,
+}
+pub type XExtData = _XExtData;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XGCValues {
+    pub function: ::std::os::raw::c_int,
+    pub plane_mask: ::std::os::raw::c_ulong,
+    pub foreground: ::std::os::raw::c_ulong,
+    pub background: ::std::os::raw::c_ulong,
+    pub line_width: ::std::os::raw::c_int,
+    pub line_style: ::std::os::raw::c_int,
+    pub cap_style: ::std::os::raw::c_int,
+    pub join_style: ::std::os::raw::c_int,
+    pub fill_style: ::std::os::raw::c_int,
+    pub fill_rule: ::std::os::raw::c_int,
+    pub arc_mode: ::std::os::raw::c_int,
+    pub tile: Pixmap,
+    pub stipple: Pixmap,
+    pub ts_x_origin: ::std::os::raw::c_int,
+    pub ts_y_origin: ::std::os::raw::c_int,
+    pub font: Font,
+    pub subwindow_mode: ::std::os::raw::c_int,
+    pub graphics_exposures: ::std::os::raw::c_int,
+    pub clip_x_origin: ::std::os::raw::c_int,
+    pub clip_y_origin: ::std::os::raw::c_int,
+    pub clip_mask: Pixmap,
+    pub dash_offset: ::std::os::raw::c_int,
+    pub dashes: ::std::os::raw::c_char,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct _XGC {
+    _unused: [u8; 0],
+}
+pub type GC = *mut _XGC;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct Visual {
+    pub ext_data: *mut XExtData,
+    pub visualid: VisualID,
+    pub class: ::std::os::raw::c_int,
+    pub red_mask: ::std::os::raw::c_ulong,
+    pub green_mask: ::std::os::raw::c_ulong,
+    pub blue_mask: ::std::os::raw::c_ulong,
+    pub bits_per_rgb: ::std::os::raw::c_int,
+    pub map_entries: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct Depth {
+    pub depth: ::std::os::raw::c_int,
+    pub nvisuals: ::std::os::raw::c_int,
+    pub visuals: *mut Visual,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct _XDisplay {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct Screen {
+    pub ext_data: *mut XExtData,
+    pub display: *mut _XDisplay,
+    pub root: Window,
+    pub width: ::std::os::raw::c_int,
+    pub height: ::std::os::raw::c_int,
+    pub mwidth: ::std::os::raw::c_int,
+    pub mheight: ::std::os::raw::c_int,
+    pub ndepths: ::std::os::raw::c_int,
+    pub depths: *mut Depth,
+    pub root_depth: ::std::os::raw::c_int,
+    pub root_visual: *mut Visual,
+    pub default_gc: GC,
+    pub cmap: Colormap,
+    pub white_pixel: ::std::os::raw::c_ulong,
+    pub black_pixel: ::std::os::raw::c_ulong,
+    pub max_maps: ::std::os::raw::c_int,
+    pub min_maps: ::std::os::raw::c_int,
+    pub backing_store: ::std::os::raw::c_int,
+    pub save_unders: ::std::os::raw::c_int,
+    pub root_input_mask: ::std::os::raw::c_long,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct _XImage {
+    pub width: ::std::os::raw::c_int,
+    pub height: ::std::os::raw::c_int,
+    pub xoffset: ::std::os::raw::c_int,
+    pub format: ::std::os::raw::c_int,
+    pub data: *mut ::std::os::raw::c_char,
+    pub byte_order: ::std::os::raw::c_int,
+    pub bitmap_unit: ::std::os::raw::c_int,
+    pub bitmap_bit_order: ::std::os::raw::c_int,
+    pub bitmap_pad: ::std::os::raw::c_int,
+    pub depth: ::std::os::raw::c_int,
+    pub bytes_per_line: ::std::os::raw::c_int,
+    pub bits_per_pixel: ::std::os::raw::c_int,
+    pub red_mask: ::std::os::raw::c_ulong,
+    pub green_mask: ::std::os::raw::c_ulong,
+    pub blue_mask: ::std::os::raw::c_ulong,
+    pub obdata: XPointer,
+    pub f: _XImage_funcs,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct _XImage_funcs {
+    pub create_image: ::std::option::Option<
+        unsafe extern "C" fn(
+            arg1: *mut _XDisplay,
+            arg2: *mut Visual,
+            arg3: ::std::os::raw::c_uint,
+            arg4: ::std::os::raw::c_int,
+            arg5: ::std::os::raw::c_int,
+            arg6: *mut ::std::os::raw::c_char,
+            arg7: ::std::os::raw::c_uint,
+            arg8: ::std::os::raw::c_uint,
+            arg9: ::std::os::raw::c_int,
+            arg10: ::std::os::raw::c_int,
+        ) -> *mut _XImage,
+    >,
+    pub destroy_image:
+        ::std::option::Option<unsafe extern "C" fn(arg1: *mut _XImage) -> ::std::os::raw::c_int>,
+    pub get_pixel: ::std::option::Option<
+        unsafe extern "C" fn(
+            arg1: *mut _XImage,
+            arg2: ::std::os::raw::c_int,
+            arg3: ::std::os::raw::c_int,
+        ) -> ::std::os::raw::c_ulong,
+    >,
+    pub put_pixel: ::std::option::Option<
+        unsafe extern "C" fn(
+            arg1: *mut _XImage,
+            arg2: ::std::os::raw::c_int,
+            arg3: ::std::os::raw::c_int,
+            arg4: ::std::os::raw::c_ulong,
+        ) -> ::std::os::raw::c_int,
+    >,
+    pub sub_image: ::std::option::Option<
+        unsafe extern "C" fn(
+            arg1: *mut _XImage,
+            arg2: ::std::os::raw::c_int,
+            arg3: ::std::os::raw::c_int,
+            arg4: ::std::os::raw::c_uint,
+            arg5: ::std::os::raw::c_uint,
+        ) -> *mut _XImage,
+    >,
+    pub add_pixel: ::std::option::Option<
+        unsafe extern "C" fn(
+            arg1: *mut _XImage,
+            arg2: ::std::os::raw::c_long,
+        ) -> ::std::os::raw::c_int,
+    >,
+}
+pub type XImage = _XImage;
+pub type Display = _XDisplay;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XKeyEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub root: Window,
+    pub subwindow: Window,
+    pub time: Time,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub x_root: ::std::os::raw::c_int,
+    pub y_root: ::std::os::raw::c_int,
+    pub state: ::std::os::raw::c_uint,
+    pub keycode: ::std::os::raw::c_uint,
+    pub same_screen: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XButtonEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub root: Window,
+    pub subwindow: Window,
+    pub time: Time,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub x_root: ::std::os::raw::c_int,
+    pub y_root: ::std::os::raw::c_int,
+    pub state: ::std::os::raw::c_uint,
+    pub button: ::std::os::raw::c_uint,
+    pub same_screen: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XMotionEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub root: Window,
+    pub subwindow: Window,
+    pub time: Time,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub x_root: ::std::os::raw::c_int,
+    pub y_root: ::std::os::raw::c_int,
+    pub state: ::std::os::raw::c_uint,
+    pub is_hint: ::std::os::raw::c_char,
+    pub same_screen: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XCrossingEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub root: Window,
+    pub subwindow: Window,
+    pub time: Time,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub x_root: ::std::os::raw::c_int,
+    pub y_root: ::std::os::raw::c_int,
+    pub mode: ::std::os::raw::c_int,
+    pub detail: ::std::os::raw::c_int,
+    pub same_screen: ::std::os::raw::c_int,
+    pub focus: ::std::os::raw::c_int,
+    pub state: ::std::os::raw::c_uint,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XFocusChangeEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub mode: ::std::os::raw::c_int,
+    pub detail: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XKeymapEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub key_vector: [::std::os::raw::c_char; 32usize],
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XExposeEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub width: ::std::os::raw::c_int,
+    pub height: ::std::os::raw::c_int,
+    pub count: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XGraphicsExposeEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub drawable: Drawable,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub width: ::std::os::raw::c_int,
+    pub height: ::std::os::raw::c_int,
+    pub count: ::std::os::raw::c_int,
+    pub major_code: ::std::os::raw::c_int,
+    pub minor_code: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XNoExposeEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub drawable: Drawable,
+    pub major_code: ::std::os::raw::c_int,
+    pub minor_code: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XVisibilityEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub state: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XCreateWindowEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub parent: Window,
+    pub window: Window,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub width: ::std::os::raw::c_int,
+    pub height: ::std::os::raw::c_int,
+    pub border_width: ::std::os::raw::c_int,
+    pub override_redirect: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XDestroyWindowEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub event: Window,
+    pub window: Window,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XUnmapEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub event: Window,
+    pub window: Window,
+    pub from_configure: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XMapEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub event: Window,
+    pub window: Window,
+    pub override_redirect: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XMapRequestEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub parent: Window,
+    pub window: Window,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XReparentEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub event: Window,
+    pub window: Window,
+    pub parent: Window,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub override_redirect: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XConfigureEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub event: Window,
+    pub window: Window,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub width: ::std::os::raw::c_int,
+    pub height: ::std::os::raw::c_int,
+    pub border_width: ::std::os::raw::c_int,
+    pub above: Window,
+    pub override_redirect: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XGravityEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub event: Window,
+    pub window: Window,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XResizeRequestEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub width: ::std::os::raw::c_int,
+    pub height: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XConfigureRequestEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub parent: Window,
+    pub window: Window,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub width: ::std::os::raw::c_int,
+    pub height: ::std::os::raw::c_int,
+    pub border_width: ::std::os::raw::c_int,
+    pub above: Window,
+    pub detail: ::std::os::raw::c_int,
+    pub value_mask: ::std::os::raw::c_ulong,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XCirculateEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub event: Window,
+    pub window: Window,
+    pub place: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XCirculateRequestEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub parent: Window,
+    pub window: Window,
+    pub place: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XPropertyEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub atom: Atom,
+    pub time: Time,
+    pub state: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XSelectionClearEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub selection: Atom,
+    pub time: Time,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XSelectionRequestEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub owner: Window,
+    pub requestor: Window,
+    pub selection: Atom,
+    pub target: Atom,
+    pub property: Atom,
+    pub time: Time,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XSelectionEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub requestor: Window,
+    pub selection: Atom,
+    pub target: Atom,
+    pub property: Atom,
+    pub time: Time,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XColormapEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub colormap: Colormap,
+    pub new: ::std::os::raw::c_int,
+    pub state: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XClientMessageEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub message_type: Atom,
+    pub format: ::std::os::raw::c_int,
+    pub data: XClientMessageEvent__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union XClientMessageEvent__bindgen_ty_1 {
+    pub b: [::std::os::raw::c_char; 20usize],
+    pub s: [::std::os::raw::c_short; 10usize],
+    pub l: [::std::os::raw::c_long; 5usize],
+    _bindgen_union_align: [u64; 5usize],
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XMappingEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+    pub request: ::std::os::raw::c_int,
+    pub first_keycode: ::std::os::raw::c_int,
+    pub count: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XErrorEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub resourceid: XID,
+    pub serial: ::std::os::raw::c_ulong,
+    pub error_code: ::std::os::raw::c_uchar,
+    pub request_code: ::std::os::raw::c_uchar,
+    pub minor_code: ::std::os::raw::c_uchar,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XAnyEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub window: Window,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XGenericEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub extension: ::std::os::raw::c_int,
+    pub evtype: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XGenericEventCookie {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub extension: ::std::os::raw::c_int,
+    pub evtype: ::std::os::raw::c_int,
+    pub cookie: ::std::os::raw::c_uint,
+    pub data: *mut ::std::os::raw::c_void,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union _XEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub xany: XAnyEvent,
+    pub xkey: XKeyEvent,
+    pub xbutton: XButtonEvent,
+    pub xmotion: XMotionEvent,
+    pub xcrossing: XCrossingEvent,
+    pub xfocus: XFocusChangeEvent,
+    pub xexpose: XExposeEvent,
+    pub xgraphicsexpose: XGraphicsExposeEvent,
+    pub xnoexpose: XNoExposeEvent,
+    pub xvisibility: XVisibilityEvent,
+    pub xcreatewindow: XCreateWindowEvent,
+    pub xdestroywindow: XDestroyWindowEvent,
+    pub xunmap: XUnmapEvent,
+    pub xmap: XMapEvent,
+    pub xmaprequest: XMapRequestEvent,
+    pub xreparent: XReparentEvent,
+    pub xconfigure: XConfigureEvent,
+    pub xgravity: XGravityEvent,
+    pub xresizerequest: XResizeRequestEvent,
+    pub xconfigurerequest: XConfigureRequestEvent,
+    pub xcirculate: XCirculateEvent,
+    pub xcirculaterequest: XCirculateRequestEvent,
+    pub xproperty: XPropertyEvent,
+    pub xselectionclear: XSelectionClearEvent,
+    pub xselectionrequest: XSelectionRequestEvent,
+    pub xselection: XSelectionEvent,
+    pub xcolormap: XColormapEvent,
+    pub xclient: XClientMessageEvent,
+    pub xmapping: XMappingEvent,
+    pub xerror: XErrorEvent,
+    pub xkeymap: XKeymapEvent,
+    pub xgeneric: XGenericEvent,
+    pub xcookie: XGenericEventCookie,
+    pub pad: [::std::os::raw::c_long; 24usize],
+    _bindgen_union_align: [u64; 24usize],
+}
+pub type XEvent = _XEvent;
+extern "C" {
+    pub fn XOpenDisplay(arg1: *const ::std::os::raw::c_char) -> *mut Display;
+}
+extern "C" {
+    pub fn XInternAtom(
+        arg1: *mut Display,
+        arg2: *const ::std::os::raw::c_char,
+        arg3: ::std::os::raw::c_int,
+    ) -> Atom;
+}
+extern "C" {
+    pub fn XCreateGC(
+        arg1: *mut Display,
+        arg2: Drawable,
+        arg3: ::std::os::raw::c_ulong,
+        arg4: *mut XGCValues,
+    ) -> GC;
+}
+extern "C" {
+    pub fn XCreateSimpleWindow(
+        arg1: *mut Display,
+        arg2: Window,
+        arg3: ::std::os::raw::c_int,
+        arg4: ::std::os::raw::c_int,
+        arg5: ::std::os::raw::c_uint,
+        arg6: ::std::os::raw::c_uint,
+        arg7: ::std::os::raw::c_uint,
+        arg8: ::std::os::raw::c_ulong,
+        arg9: ::std::os::raw::c_ulong,
+    ) -> Window;
+}
+extern "C" {
+    pub fn XRootWindowOfScreen(arg1: *mut Screen) -> Window;
+}
+extern "C" {
+    pub fn XDefaultVisualOfScreen(arg1: *mut Screen) -> *mut Visual;
+}
+extern "C" {
+    pub fn XBlackPixelOfScreen(arg1: *mut Screen) -> ::std::os::raw::c_ulong;
+}
+extern "C" {
+    pub fn XDefaultScreenOfDisplay(arg1: *mut Display) -> *mut Screen;
+}
+extern "C" {
+    pub fn XScreenNumberOfScreen(arg1: *mut Screen) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XSetWMProtocols(
+        arg1: *mut Display,
+        arg2: Window,
+        arg3: *mut Atom,
+        arg4: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XClearWindow(arg1: *mut Display, arg2: Window) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XCloseDisplay(arg1: *mut Display) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XConnectionNumber(arg1: *mut Display) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XDefaultDepthOfScreen(arg1: *mut Screen) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XDestroyWindow(arg1: *mut Display, arg2: Window) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XFlush(arg1: *mut Display) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XFree(arg1: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XFreeGC(arg1: *mut Display, arg2: GC) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XMapRaised(arg1: *mut Display, arg2: Window) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XNextEvent(arg1: *mut Display, arg2: *mut XEvent) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XPending(arg1: *mut Display) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XSelectInput(
+        arg1: *mut Display,
+        arg2: Window,
+        arg3: ::std::os::raw::c_long,
+    ) -> ::std::os::raw::c_int;
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XSizeHints {
+    pub flags: ::std::os::raw::c_long,
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+    pub width: ::std::os::raw::c_int,
+    pub height: ::std::os::raw::c_int,
+    pub min_width: ::std::os::raw::c_int,
+    pub min_height: ::std::os::raw::c_int,
+    pub max_width: ::std::os::raw::c_int,
+    pub max_height: ::std::os::raw::c_int,
+    pub width_inc: ::std::os::raw::c_int,
+    pub height_inc: ::std::os::raw::c_int,
+    pub min_aspect: XSizeHints__bindgen_ty_1,
+    pub max_aspect: XSizeHints__bindgen_ty_1,
+    pub base_width: ::std::os::raw::c_int,
+    pub base_height: ::std::os::raw::c_int,
+    pub win_gravity: ::std::os::raw::c_int,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XSizeHints__bindgen_ty_1 {
+    pub x: ::std::os::raw::c_int,
+    pub y: ::std::os::raw::c_int,
+}
+extern "C" {
+    pub fn XDestroyImage(ximage: *mut XImage) -> ::std::os::raw::c_int;
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XVisualInfo {
+    pub visual: *mut Visual,
+    pub visualid: VisualID,
+    pub screen: ::std::os::raw::c_int,
+    pub depth: ::std::os::raw::c_int,
+    pub class: ::std::os::raw::c_int,
+    pub red_mask: ::std::os::raw::c_ulong,
+    pub green_mask: ::std::os::raw::c_ulong,
+    pub blue_mask: ::std::os::raw::c_ulong,
+    pub colormap_size: ::std::os::raw::c_int,
+    pub bits_per_rgb: ::std::os::raw::c_int,
+}
+extern "C" {
+    pub fn XAllocSizeHints() -> *mut XSizeHints;
+}
+extern "C" {
+    pub fn XGetVisualInfo(
+        arg1: *mut Display,
+        arg2: ::std::os::raw::c_long,
+        arg3: *mut XVisualInfo,
+        arg4: *mut ::std::os::raw::c_int,
+    ) -> *mut XVisualInfo;
+}
+extern "C" {
+    pub fn XSetWMNormalHints(arg1: *mut Display, arg2: Window, arg3: *mut XSizeHints);
+}
+pub type ShmSeg = ::std::os::raw::c_ulong;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XShmCompletionEvent {
+    pub type_: ::std::os::raw::c_int,
+    pub serial: ::std::os::raw::c_ulong,
+    pub send_event: ::std::os::raw::c_int,
+    pub display: *mut Display,
+    pub drawable: Drawable,
+    pub major_code: ::std::os::raw::c_int,
+    pub minor_code: ::std::os::raw::c_int,
+    pub shmseg: ShmSeg,
+    pub offset: ::std::os::raw::c_ulong,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct XShmSegmentInfo {
+    pub shmseg: ShmSeg,
+    pub shmid: ::std::os::raw::c_int,
+    pub shmaddr: *mut ::std::os::raw::c_char,
+    pub readOnly: ::std::os::raw::c_int,
+}
+extern "C" {
+    pub fn XShmQueryExtension(arg1: *mut Display) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XShmGetEventBase(arg1: *mut Display) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XShmAttach(arg1: *mut Display, arg2: *mut XShmSegmentInfo) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XShmDetach(arg1: *mut Display, arg2: *mut XShmSegmentInfo) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XShmPutImage(
+        arg1: *mut Display,
+        arg2: Drawable,
+        arg3: GC,
+        arg4: *mut XImage,
+        arg5: ::std::os::raw::c_int,
+        arg6: ::std::os::raw::c_int,
+        arg7: ::std::os::raw::c_int,
+        arg8: ::std::os::raw::c_int,
+        arg9: ::std::os::raw::c_uint,
+        arg10: ::std::os::raw::c_uint,
+        arg11: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn XShmCreateImage(
+        arg1: *mut Display,
+        arg2: *mut Visual,
+        arg3: ::std::os::raw::c_uint,
+        arg4: ::std::os::raw::c_int,
+        arg5: *mut ::std::os::raw::c_char,
+        arg6: *mut XShmSegmentInfo,
+        arg7: ::std::os::raw::c_uint,
+        arg8: ::std::os::raw::c_uint,
+    ) -> *mut XImage;
+}
diff --git a/gpu_display/src/generated/xlib_generator.sh b/gpu_display/src/generated/xlib_generator.sh
new file mode 100755
index 0000000..b915b33
--- /dev/null
+++ b/gpu_display/src/generated/xlib_generator.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+# 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.
+
+cd "${0%/*}"
+
+cat >xlib.rs <<EOF
+// 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.
+
+//! Generated using ./xlib_generator.sh
+
+#[link(name = "X11")]
+extern "C" {}
+
+#[link(name = "Xext")]
+extern "C" {}
+
+EOF
+
+bindgen --no-layout-tests --no-derive-debug \
+  --whitelist-function XAllocSizeHints \
+  --whitelist-function XBlackPixelOfScreen \
+  --whitelist-function XClearWindow \
+  --whitelist-function XCloseDisplay \
+  --whitelist-function XConnectionNumber \
+  --whitelist-function XCreateGC \
+  --whitelist-function XCreateSimpleWindow \
+  --whitelist-function XDefaultDepthOfScreen \
+  --whitelist-function XDefaultScreenOfDisplay \
+  --whitelist-function XDefaultVisualOfScreen \
+  --whitelist-function XDestroyImage \
+  --whitelist-function XDestroyWindow \
+  --whitelist-function XFlush \
+  --whitelist-function XFree \
+  --whitelist-function XFreeGC \
+  --whitelist-function XGetVisualInfo \
+  --whitelist-function XInternAtom \
+  --whitelist-function XMapRaised \
+  --whitelist-function XNextEvent \
+  --whitelist-function XOpenDisplay \
+  --whitelist-function XPending \
+  --whitelist-function XRootWindowOfScreen \
+  --whitelist-function XScreenNumberOfScreen \
+  --whitelist-function XSelectInput \
+  --whitelist-function XSetWMNormalHints \
+  --whitelist-function XSetWMProtocols \
+  --whitelist-function XShmAttach \
+  --whitelist-function XShmCreateImage \
+  --whitelist-function XShmDetach \
+  --whitelist-function XShmGetEventBase \
+  --whitelist-function XShmPutImage \
+  --whitelist-function XShmQueryExtension \
+  --whitelist-var ClientMessage \
+  --whitelist-var Expose \
+  --whitelist-var ExposureMask \
+  --whitelist-var PMaxSize \
+  --whitelist-var PMinSize \
+  --whitelist-var ShmCompletion \
+  --whitelist-var VisualBlueMaskMask \
+  --whitelist-var VisualDepthMask \
+  --whitelist-var VisualGreenMaskMask \
+  --whitelist-var VisualRedMaskMask \
+  --whitelist-var VisualScreenMask \
+  --whitelist-var ZPixmap \
+  --whitelist-type Display \
+  --whitelist-type GC \
+  --whitelist-type Screen \
+  --whitelist-type XShmCompletionEvent \
+  --whitelist-type ShmSeg \
+  --whitelist-type Visual \
+  --whitelist-type Window \
+  --whitelist-type XVisualInfo \
+  xlib_wrapper.h >>xlib.rs
diff --git a/gpu_display/src/generated/xlib_wrapper.h b/gpu_display/src/generated/xlib_wrapper.h
new file mode 100644
index 0000000..2084799
--- /dev/null
+++ b/gpu_display/src/generated/xlib_wrapper.h
@@ -0,0 +1,4 @@
+#define XUTIL_DEFINE_FUNCTIONS
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
diff --git a/gpu_display/src/gpu_display_wl.rs b/gpu_display/src/gpu_display_wl.rs
new file mode 100644
index 0000000..a079d87
--- /dev/null
+++ b/gpu_display/src/gpu_display_wl.rs
@@ -0,0 +1,347 @@
+// 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.
+
+//! Crate for displaying simple surfaces and GPU buffers over wayland.
+
+extern crate data_model;
+extern crate sys_util;
+
+#[path = "dwl.rs"]
+mod dwl;
+
+use dwl::*;
+
+use crate::{DisplayT, GpuDisplayError, GpuDisplayFramebuffer};
+
+use std::cell::Cell;
+use std::collections::HashMap;
+use std::ffi::{CStr, CString};
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::path::Path;
+use std::ptr::{null, null_mut};
+
+use data_model::VolatileMemory;
+use sys_util::{round_up_to_page_size, MemoryMapping, SharedMemory};
+
+const BUFFER_COUNT: usize = 2;
+const BYTES_PER_PIXEL: u32 = 4;
+
+struct DwlContext(*mut dwl_context);
+impl Drop for DwlContext {
+    fn drop(&mut self) {
+        if !self.0.is_null() {
+            // 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);
+            }
+        }
+    }
+}
+
+struct DwlDmabuf(*mut dwl_dmabuf);
+impl Drop for DwlDmabuf {
+    fn drop(&mut self) {
+        if !self.0.is_null() {
+            // 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() {
+            // 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 Surface {
+    surface: DwlSurface,
+    row_size: u32,
+    buffer_size: usize,
+    buffer_index: Cell<usize>,
+    buffer_mem: MemoryMapping,
+}
+
+impl Surface {
+    fn surface(&self) -> *mut dwl_surface {
+        self.surface.0
+    }
+}
+
+/// A connection to the compositor and associated collection of state.
+///
+/// The user of `GpuDisplay` can use `AsRawFd` 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 {
+    ctx: DwlContext,
+    dmabufs: HashMap<u32, DwlDmabuf>,
+    dmabuf_next_id: u32,
+    surfaces: HashMap<u32, Surface>,
+    surface_next_id: u32,
+}
+
+impl DisplayWl {
+    /// Opens a fresh connection to the compositor.
+    pub fn new(wayland_path: Option<&Path>) -> Result<DisplayWl, GpuDisplayError> {
+        // The dwl_context_new call should always be safe to call, and we check its result.
+        let ctx = DwlContext(unsafe { dwl_context_new() });
+        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,
+        };
+        let setup_success =
+            unsafe { dwl_context_setup(ctx.0, cstr_path.map(|s| s.as_ptr()).unwrap_or(null())) };
+        if !setup_success {
+            return Err(GpuDisplayError::Connect);
+        }
+
+        Ok(DisplayWl {
+            ctx,
+            dmabufs: Default::default(),
+            dmabuf_next_id: 0,
+            surfaces: Default::default(),
+            surface_next_id: 0,
+        })
+    }
+
+    fn ctx(&self) -> *mut dwl_context {
+        self.ctx.0
+    }
+
+    fn get_surface(&self, surface_id: u32) -> Option<&Surface> {
+        self.surfaces.get(&surface_id)
+    }
+}
+
+impl DisplayT for DisplayWl {
+    fn import_dmabuf(
+        &mut self,
+        fd: RawFd,
+        offset: u32,
+        stride: u32,
+        modifiers: u64,
+        width: u32,
+        height: u32,
+        fourcc: u32,
+    ) -> Result<u32, GpuDisplayError> {
+        // 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(),
+                fd,
+                offset,
+                stride,
+                modifiers,
+                width,
+                height,
+                fourcc,
+            )
+        });
+        if dmabuf.0.is_null() {
+            return Err(GpuDisplayError::FailedImport);
+        }
+
+        let next_id = self.dmabuf_next_id;
+        self.dmabufs.insert(next_id, dmabuf);
+        self.dmabuf_next_id += 1;
+        Ok(next_id)
+    }
+
+    fn release_import(&mut self, import_id: u32) {
+        self.dmabufs.remove(&import_id);
+    }
+
+    fn dispatch_events(&mut self) {
+        // Safe given that the context pointer is valid.
+        unsafe {
+            dwl_context_dispatch(self.ctx());
+        }
+    }
+
+    fn create_surface(
+        &mut self,
+        parent_surface_id: Option<u32>,
+        width: u32,
+        height: u32,
+    ) -> Result<u32, GpuDisplayError> {
+        let parent_ptr = match parent_surface_id {
+            Some(id) => match self.get_surface(id).map(|p| p.surface()) {
+                Some(ptr) => ptr,
+                None => return Err(GpuDisplayError::InvalidSurfaceId),
+            },
+            None => null_mut(),
+        };
+        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 mut buffer_shm = SharedMemory::new(Some(
+            CStr::from_bytes_with_nul(b"GpuDisplaySurface\0").unwrap(),
+        ))
+        .map_err(GpuDisplayError::CreateShm)?;
+        buffer_shm
+            .set_size(buffer_size as u64)
+            .map_err(GpuDisplayError::SetSize)?;
+        let buffer_mem = MemoryMapping::from_fd(&buffer_shm, buffer_size).unwrap();
+
+        // Safe because only a valid context, parent pointer (if not  None), 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_ptr,
+                buffer_shm.as_raw_fd(),
+                buffer_size,
+                fb_size as usize,
+                width,
+                height,
+                row_size,
+            )
+        });
+
+        if surface.0.is_null() {
+            return Err(GpuDisplayError::CreateSurface);
+        }
+
+        let next_id = self.surface_next_id;
+        self.surfaces.insert(
+            next_id,
+            Surface {
+                surface,
+                row_size,
+                buffer_size: fb_size as usize,
+                buffer_index: Cell::new(0),
+                buffer_mem,
+            },
+        );
+
+        self.surface_next_id += 1;
+        Ok(next_id)
+    }
+
+    fn release_surface(&mut self, surface_id: u32) {
+        self.surfaces.remove(&surface_id);
+    }
+
+    fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer> {
+        let surface = self.get_surface(surface_id)?;
+        let buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT;
+        let framebuffer = surface
+            .buffer_mem
+            .get_slice(
+                (buffer_index * surface.buffer_size) as u64,
+                surface.buffer_size as u64,
+            )
+            .ok()?;
+        Some(GpuDisplayFramebuffer::new(
+            framebuffer,
+            surface.row_size,
+            BYTES_PER_PIXEL,
+        ))
+    }
+
+    fn commit(&mut self, surface_id: u32) {
+        match self.get_surface(surface_id) {
+            Some(surface) => {
+                // Safe because only a valid surface is used.
+                unsafe {
+                    dwl_surface_commit(surface.surface());
+                }
+            }
+            None => debug_assert!(false, "invalid surface_id {}", surface_id),
+        }
+    }
+
+    fn next_buffer_in_use(&self, surface_id: u32) -> bool {
+        match self.get_surface(surface_id) {
+            Some(surface) => {
+                let next_buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT;
+                // Safe because only a valid surface and buffer index is used.
+                unsafe { dwl_surface_buffer_in_use(surface.surface(), next_buffer_index) }
+            }
+            None => {
+                debug_assert!(false, "invalid surface_id {}", surface_id);
+                false
+            }
+        }
+    }
+
+    fn flip(&mut self, surface_id: u32) {
+        match self.get_surface(surface_id) {
+            Some(surface) => {
+                surface
+                    .buffer_index
+                    .set((surface.buffer_index.get() + 1) % BUFFER_COUNT);
+                // Safe because only a valid surface and buffer index is used.
+                unsafe {
+                    dwl_surface_flip(surface.surface(), surface.buffer_index.get());
+                }
+            }
+            None => debug_assert!(false, "invalid surface_id {}", surface_id),
+        }
+    }
+
+    fn flip_to(&mut self, surface_id: u32, import_id: u32) {
+        match self.get_surface(surface_id) {
+            Some(surface) => {
+                match self.dmabufs.get(&import_id) {
+                    // Safe because only a valid surface and dmabuf is used.
+                    Some(dmabuf) => unsafe { dwl_surface_flip_to(surface.surface(), dmabuf.0) },
+                    None => debug_assert!(false, "invalid import_id {}", import_id),
+                }
+            }
+            None => debug_assert!(false, "invalid surface_id {}", surface_id),
+        }
+    }
+
+    fn close_requested(&self, surface_id: u32) -> bool {
+        match self.get_surface(surface_id) {
+            Some(surface) =>
+            // Safe because only a valid surface is used.
+            unsafe { dwl_surface_close_requested(surface.surface()) }
+            None => false,
+        }
+    }
+
+    fn set_position(&mut self, surface_id: u32, x: u32, y: u32) {
+        match self.get_surface(surface_id) {
+            Some(surface) => {
+                // Safe because only a valid surface is used.
+                unsafe {
+                    dwl_surface_set_position(surface.surface(), x, y);
+                }
+            }
+            None => debug_assert!(false, "invalid surface_id {}", surface_id),
+        }
+    }
+}
+
+impl AsRawFd for DisplayWl {
+    fn as_raw_fd(&self) -> RawFd {
+        // Safe given that the context pointer is valid.
+        unsafe { dwl_context_fd(self.ctx.0) }
+    }
+}
diff --git a/gpu_display/src/gpu_display_x.rs b/gpu_display/src/gpu_display_x.rs
new file mode 100644
index 0000000..62ee505
--- /dev/null
+++ b/gpu_display/src/gpu_display_x.rs
@@ -0,0 +1,647 @@
+// 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 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 libc::{shmat, shmctl, shmdt, shmget, IPC_CREAT, IPC_PRIVATE, IPC_RMID};
+
+use crate::{DisplayT, GpuDisplayError, GpuDisplayFramebuffer};
+
+use data_model::VolatileSlice;
+
+const BUFFER_COUNT: usize = 2;
+
+type SurfaceId = 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::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 {
+    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,
+
+    // 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> {
+        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 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,
+                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 handle_event(&mut self, ev: XEvent) {
+        match ev.as_enum(self.buffer_completion_type) {
+            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);
+        }
+    }
+}
+
+pub struct DisplayX {
+    display: XDisplay,
+    screen: XScreen,
+    visual: *mut xlib::Visual,
+    next_surface_id: SurfaceId,
+    surfaces: BTreeMap<SurfaceId, Surface>,
+}
+
+impl DisplayX {
+    pub fn open_display(display: Option<&str>) -> Result<DisplayX, GpuDisplayError> {
+        let display_cstr = match display.map(|s| CString::new(s)) {
+            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),
+            };
+
+            // 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 {
+                display,
+                screen,
+                visual,
+                next_surface_id: SurfaceId::new(1).unwrap(),
+                surfaces: Default::default(),
+            })
+        }
+    }
+
+    fn surface_ref(&self, surface_id: u32) -> Option<&Surface> {
+        SurfaceId::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))
+    }
+
+    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;
+        }
+    }
+}
+
+impl DisplayT for DisplayX {
+    fn dispatch_events(&mut self) {
+        loop {
+            self.display.flush();
+            if !self.display.pending_events() {
+                break;
+            }
+            let ev = self.display.next_event();
+            self.handle_event(ev);
+        }
+    }
+
+    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_surface_id;
+        self.surfaces.insert(new_surface_id, new_surface);
+        self.next_surface_id = SurfaceId::new(self.next_surface_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));
+    }
+
+    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> {
+        return 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
+    }
+}
+
+impl AsRawFd for DisplayX {
+    fn as_raw_fd(&self) -> RawFd {
+        self.display.as_raw_fd()
+    }
+}
diff --git a/gpu_display/src/lib.rs b/gpu_display/src/lib.rs
index 11a6703..1bf9578 100644
--- a/gpu_display/src/lib.rs
+++ b/gpu_display/src/lib.rs
@@ -4,23 +4,16 @@
 
 //! Crate for displaying simple surfaces and GPU buffers over wayland.
 
-mod dwl;
-
-use std::cell::Cell;
-use std::collections::HashMap;
-use std::ffi::{CStr, CString};
 use std::fmt::{self, Display};
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::path::Path;
-use std::ptr::null_mut;
-
-use data_model::{VolatileMemory, VolatileSlice};
-use sys_util::{round_up_to_page_size, Error as SysError, MemoryMapping, SharedMemory};
 
-use crate::dwl::*;
+use data_model::VolatileSlice;
+use sys_util::Error as SysError;
 
-const BUFFER_COUNT: usize = 2;
-const BYTES_PER_PIXEL: u32 = 4;
+mod gpu_display_wl;
+#[cfg(feature = "x")]
+mod gpu_display_x;
 
 /// An error generated by `GpuDisplay`.
 #[derive(Debug)]
@@ -39,8 +32,12 @@ pub enum GpuDisplayError {
     FailedImport,
     /// The surface ID is invalid.
     InvalidSurfaceId,
+    /// A required feature was missing.
+    RequiredFeature(&'static str),
     /// The path is invalid.
     InvalidPath,
+    /// The method is unsupported by the implementation.
+    Unsupported,
 }
 
 impl Display for GpuDisplayError {
@@ -51,65 +48,110 @@ impl Display for GpuDisplayError {
             Allocate => write!(f, "internal allocation failed"),
             Connect => write!(f, "failed to connect to compositor"),
             CreateShm(e) => write!(f, "failed to create shared memory: {}", e),
-            SetSize(e) => write!(f, "failed to set size of shared memory: {}", e),
             CreateSurface => write!(f, "failed to crate surface on the compositor"),
             FailedImport => write!(f, "failed to import a buffer to the compositor"),
-            InvalidSurfaceId => write!(f, "invalid surface ID"),
             InvalidPath => write!(f, "invalid path"),
+            InvalidSurfaceId => write!(f, "invalid surface ID"),
+            RequiredFeature(feature) => write!(f, "required feature was missing: {}", feature),
+            SetSize(e) => write!(f, "failed to set size of shared memory: {}", e),
+            Unsupported => write!(f, "unsupported by the implementation"),
         }
     }
 }
 
-struct DwlContext(*mut dwl_context);
-impl Drop for DwlContext {
-    fn drop(&mut self) {
-        if !self.0.is_null() {
-            // 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);
-            }
-        }
-    }
+#[derive(Clone)]
+pub struct GpuDisplayFramebuffer<'a> {
+    framebuffer: VolatileSlice<'a>,
+    slice: VolatileSlice<'a>,
+    stride: u32,
+    bytes_per_pixel: u32,
 }
 
-struct DwlDmabuf(*mut dwl_dmabuf);
-impl Drop for DwlDmabuf {
-    fn drop(&mut self) {
-        if !self.0.is_null() {
-            // 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);
-            }
+impl<'a> GpuDisplayFramebuffer<'a> {
+    fn new(
+        framebuffer: VolatileSlice<'a>,
+        stride: u32,
+        bytes_per_pixel: u32,
+    ) -> GpuDisplayFramebuffer {
+        GpuDisplayFramebuffer {
+            framebuffer,
+            slice: framebuffer,
+            stride,
+            bytes_per_pixel,
         }
     }
-}
 
-struct DwlSurface(*mut dwl_surface);
-impl Drop for DwlSurface {
-    fn drop(&mut self) {
-        if !self.0.is_null() {
-            // 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);
-            }
-        }
+    fn sub_region(
+        &self,
+        x: u32,
+        y: u32,
+        width: u32,
+        height: u32,
+    ) -> Option<GpuDisplayFramebuffer<'a>> {
+        let x_byte_offset = x.checked_mul(self.bytes_per_pixel)?;
+        let y_byte_offset = y.checked_mul(self.stride)?;
+        let byte_offset = x_byte_offset.checked_add(y_byte_offset)?;
+
+        let width_bytes = width.checked_mul(self.bytes_per_pixel)?;
+        let count = height
+            .checked_mul(self.stride)?
+            .checked_sub(self.stride)?
+            .checked_add(width_bytes)?;
+        let slice = self
+            .framebuffer
+            .sub_slice(byte_offset as u64, count as u64)
+            .unwrap();
+
+        Some(GpuDisplayFramebuffer { slice, ..*self })
+    }
+
+    pub fn as_volatile_slice(&self) -> VolatileSlice<'a> {
+        self.slice
     }
-}
 
-struct GpuDisplaySurface {
-    surface: DwlSurface,
-    buffer_size: usize,
-    buffer_index: Cell<usize>,
-    buffer_mem: MemoryMapping,
+    pub fn stride(&self) -> u32 {
+        self.stride
+    }
 }
 
-impl GpuDisplaySurface {
-    fn surface(&self) -> *mut dwl_surface {
-        self.surface.0
+trait DisplayT: AsRawFd {
+    fn import_dmabuf(
+        &mut self,
+        fd: RawFd,
+        offset: u32,
+        stride: u32,
+        modifiers: u64,
+        width: u32,
+        height: u32,
+        fourcc: u32,
+    ) -> Result<u32, GpuDisplayError>;
+    fn release_import(&mut self, import_id: u32);
+    fn dispatch_events(&mut self);
+    fn create_surface(
+        &mut self,
+        parent_surface_id: Option<u32>,
+        width: u32,
+        height: u32,
+    ) -> Result<u32, GpuDisplayError>;
+    fn release_surface(&mut self, surface_id: u32);
+    fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer>;
+    fn framebuffer_region(
+        &mut self,
+        surface_id: u32,
+        x: u32,
+        y: u32,
+        width: u32,
+        height: u32,
+    ) -> Option<GpuDisplayFramebuffer> {
+        let framebuffer = self.framebuffer(surface_id)?;
+        framebuffer.sub_region(x, y, width, height)
     }
+    fn commit(&mut self, surface_id: u32);
+    fn next_buffer_in_use(&self, surface_id: u32) -> bool;
+    fn flip(&mut self, surface_id: u32);
+    fn flip_to(&mut self, surface_id: u32, import_id: u32);
+    fn close_requested(&self, surface_id: u32) -> bool;
+    fn set_position(&mut self, surface_id: u32, x: u32, y: u32);
 }
 
 /// A connection to the compositor and associated collection of state.
@@ -117,51 +159,35 @@ impl GpuDisplaySurface {
 /// The user of `GpuDisplay` can use `AsRawFd` to poll on the compositor connection's file
 /// descriptor. When the connection is readable, `dispatch_events` can be called to process it.
 pub struct GpuDisplay {
-    ctx: DwlContext,
-    dmabufs: HashMap<u32, DwlDmabuf>,
-    dmabuf_next_id: u32,
-    surfaces: HashMap<u32, GpuDisplaySurface>,
-    surface_next_id: u32,
+    inner: Box<DisplayT>,
 }
 
 impl GpuDisplay {
-    /// Opens a fresh connection to the compositor.
-    pub fn new<P: AsRef<Path>>(wayland_path: P) -> Result<GpuDisplay, GpuDisplayError> {
-        // The dwl_context_new call should always be safe to call, and we check its result.
-        let ctx = DwlContext(unsafe { dwl_context_new() });
-        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.as_ref().as_os_str().to_str() {
-            Some(str) => match CString::new(str) {
-                Ok(cstr) => cstr,
-                Err(_) => return Err(GpuDisplayError::InvalidPath),
-            },
-            None => return Err(GpuDisplayError::InvalidPath),
-        };
-        let setup_success = unsafe { dwl_context_setup(ctx.0, cstr_path.as_ptr()) };
-        if !setup_success {
-            return Err(GpuDisplayError::Connect);
+    pub fn open_x<S: AsRef<str>>(display_name: Option<S>) -> Result<GpuDisplay, GpuDisplayError> {
+        let _ = display_name;
+        #[cfg(feature = "x")]
+        {
+            let display = match display_name {
+                Some(s) => gpu_display_x::DisplayX::open_display(Some(s.as_ref()))?,
+                None => gpu_display_x::DisplayX::open_display(None)?,
+            };
+            let inner = Box::new(display);
+            Ok(GpuDisplay { inner })
         }
-
-        Ok(GpuDisplay {
-            ctx,
-            dmabufs: Default::default(),
-            dmabuf_next_id: 0,
-            surfaces: Default::default(),
-            surface_next_id: 0,
-        })
-    }
-
-    fn ctx(&self) -> *mut dwl_context {
-        self.ctx.0
+        #[cfg(not(feature = "x"))]
+        Err(GpuDisplayError::Unsupported)
     }
 
-    fn get_surface(&self, surface_id: u32) -> Option<&GpuDisplaySurface> {
-        self.surfaces.get(&surface_id)
+    /// Opens a fresh connection to the compositor.
+    pub fn open_wayland<P: AsRef<Path>>(
+        wayland_path: Option<P>,
+    ) -> Result<GpuDisplay, GpuDisplayError> {
+        let display = match wayland_path {
+            Some(s) => gpu_display_wl::DisplayWl::new(Some(s.as_ref()))?,
+            None => gpu_display_wl::DisplayWl::new(None)?,
+        };
+        let inner = Box::new(display);
+        Ok(GpuDisplay { inner })
     }
 
     /// Imports a dmabuf to the compositor for use as a surface buffer and returns a handle to it.
@@ -175,43 +201,19 @@ impl GpuDisplay {
         height: u32,
         fourcc: u32,
     ) -> Result<u32, GpuDisplayError> {
-        // 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(),
-                fd,
-                offset,
-                stride,
-                modifiers,
-                width,
-                height,
-                fourcc,
-            )
-        });
-        if dmabuf.0.is_null() {
-            return Err(GpuDisplayError::FailedImport);
-        }
-
-        let next_id = self.dmabuf_next_id;
-        self.dmabufs.insert(next_id, dmabuf);
-        self.dmabuf_next_id += 1;
-        Ok(next_id)
+        self.inner
+            .import_dmabuf(fd, offset, stride, modifiers, width, height, fourcc)
     }
 
     /// Releases a previously imported dmabuf identified by the given handle.
     pub fn release_import(&mut self, import_id: u32) {
-        self.dmabufs.remove(&import_id);
+        self.inner.release_import(import_id);
     }
 
     /// Dispatches internal events that were received from the compositor since the last call to
     /// `dispatch_events`.
     pub fn dispatch_events(&mut self) {
-        // Safe given that the context pointer is valid.
-        unsafe {
-            dwl_context_dispatch(self.ctx());
-        }
+        self.inner.dispatch_events()
     }
 
     /// Creates a surface on the the compositor as either a top level window, or child of another
@@ -222,88 +224,35 @@ impl GpuDisplay {
         width: u32,
         height: u32,
     ) -> Result<u32, GpuDisplayError> {
-        let parent_ptr = match parent_surface_id {
-            Some(id) => match self.get_surface(id).map(|p| p.surface()) {
-                Some(ptr) => ptr,
-                None => return Err(GpuDisplayError::InvalidSurfaceId),
-            },
-            None => null_mut(),
-        };
-        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 mut buffer_shm = SharedMemory::new(Some(
-            CStr::from_bytes_with_nul(b"GpuDisplaySurface\0").unwrap(),
-        ))
-        .map_err(GpuDisplayError::CreateShm)?;
-        buffer_shm
-            .set_size(buffer_size as u64)
-            .map_err(GpuDisplayError::SetSize)?;
-        let buffer_mem = MemoryMapping::from_fd(&buffer_shm, buffer_size).unwrap();
-
-        // Safe because only a valid context, parent pointer (if not  None), 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_ptr,
-                buffer_shm.as_raw_fd(),
-                buffer_size,
-                fb_size as usize,
-                width,
-                height,
-                row_size,
-            )
-        });
-
-        if surface.0.is_null() {
-            return Err(GpuDisplayError::CreateSurface);
-        }
-
-        let next_id = self.surface_next_id;
-        self.surfaces.insert(
-            next_id,
-            GpuDisplaySurface {
-                surface,
-                buffer_size: fb_size as usize,
-                buffer_index: Cell::new(0),
-                buffer_mem,
-            },
-        );
-
-        self.surface_next_id += 1;
-        Ok(next_id)
+        self.inner.create_surface(parent_surface_id, width, height)
     }
 
     /// Releases a previously created surface identified by the given handle.
     pub fn release_surface(&mut self, surface_id: u32) {
-        self.surfaces.remove(&surface_id);
+        self.inner.release_surface(surface_id)
     }
 
     /// Gets a reference to an unused framebuffer for the identified surface.
-    pub fn framebuffer_memory(&self, surface_id: u32) -> Option<VolatileSlice> {
-        let surface = self.get_surface(surface_id)?;
-        let buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT;
-        surface
-            .buffer_mem
-            .get_slice(
-                (buffer_index * surface.buffer_size) as u64,
-                surface.buffer_size as u64,
-            )
-            .ok()
+    pub fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer> {
+        self.inner.framebuffer(surface_id)
+    }
+
+    /// Gets a reference to an unused framebuffer for the identified surface.
+    pub fn framebuffer_region(
+        &mut self,
+        surface_id: u32,
+        x: u32,
+        y: u32,
+        width: u32,
+        height: u32,
+    ) -> Option<GpuDisplayFramebuffer> {
+        self.inner
+            .framebuffer_region(surface_id, x, y, width, height)
     }
 
     /// Commits any pending state for the identified surface.
-    pub fn commit(&self, surface_id: u32) {
-        match self.get_surface(surface_id) {
-            Some(surface) => {
-                // Safe because only a valid surface is used.
-                unsafe {
-                    dwl_surface_commit(surface.surface());
-                }
-            }
-            None => debug_assert!(false, "invalid surface_id {}", surface_id),
-        }
+    pub fn commit(&mut self, surface_id: u32) {
+        self.inner.commit(surface_id)
     }
 
     /// Returns true if the next buffer in the buffer queue for the given surface is currently in
@@ -312,88 +261,37 @@ impl GpuDisplay {
     /// If the next buffer is in use, the memory returned from `framebuffer_memory` should not be
     /// written to.
     pub fn next_buffer_in_use(&self, surface_id: u32) -> bool {
-        match self.get_surface(surface_id) {
-            Some(surface) => {
-                let next_buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT;
-                // Safe because only a valid surface and buffer index is used.
-                unsafe { dwl_surface_buffer_in_use(surface.surface(), next_buffer_index) }
-            }
-            None => {
-                debug_assert!(false, "invalid surface_id {}", surface_id);
-                false
-            }
-        }
+        self.inner.next_buffer_in_use(surface_id)
     }
 
     /// Changes the visible contents of the identified surface to the contents of the framebuffer
     /// last returned by `framebuffer_memory` for this surface.
-    pub fn flip(&self, surface_id: u32) {
-        match self.get_surface(surface_id) {
-            Some(surface) => {
-                surface
-                    .buffer_index
-                    .set((surface.buffer_index.get() + 1) % BUFFER_COUNT);
-                // Safe because only a valid surface and buffer index is used.
-                unsafe {
-                    dwl_surface_flip(surface.surface(), surface.buffer_index.get());
-                }
-            }
-            None => debug_assert!(false, "invalid surface_id {}", surface_id),
-        }
+    pub fn flip(&mut self, surface_id: u32) {
+        self.inner.flip(surface_id)
     }
 
     /// Changes the visible contents of the identified surface to that of the identified imported
     /// buffer.
-    pub fn flip_to(&self, surface_id: u32, import_id: u32) {
-        match self.get_surface(surface_id) {
-            Some(surface) => {
-                match self.dmabufs.get(&import_id) {
-                    // Safe because only a valid surface and dmabuf is used.
-                    Some(dmabuf) => unsafe { dwl_surface_flip_to(surface.surface(), dmabuf.0) },
-                    None => debug_assert!(false, "invalid import_id {}", import_id),
-                }
-            }
-            None => debug_assert!(false, "invalid surface_id {}", surface_id),
-        }
+    pub fn flip_to(&mut self, surface_id: u32, import_id: u32) {
+        self.inner.flip_to(surface_id, import_id)
     }
 
     /// Returns true if the identified top level surface has been told to close by the compositor,
     /// and by extension the user.
     pub fn close_requested(&self, surface_id: u32) -> bool {
-        match self.get_surface(surface_id) {
-            Some(surface) =>
-            // Safe because only a valid surface is used.
-            unsafe { dwl_surface_close_requested(surface.surface()) }
-            None => false,
-        }
+        self.inner.close_requested(surface_id)
     }
 
     /// Sets the position of the identified subsurface relative to its parent.
     ///
     /// The change in position will not be visible until `commit` is called for the parent surface.
-    pub fn set_position(&self, surface_id: u32, x: u32, y: u32) {
-        match self.get_surface(surface_id) {
-            Some(surface) => {
-                // Safe because only a valid surface is used.
-                unsafe {
-                    dwl_surface_set_position(surface.surface(), x, y);
-                }
-            }
-            None => debug_assert!(false, "invalid surface_id {}", surface_id),
-        }
-    }
-}
-
-impl Drop for GpuDisplay {
-    fn drop(&mut self) {
-        // Safe given that the context pointer is valid.
-        unsafe { dwl_context_destroy(&mut self.ctx.0) }
+    pub fn set_position(&mut self, surface_id: u32, x: u32, y: u32) {
+        self.inner.set_position(surface_id, x, y)
     }
 }
 
 impl AsRawFd for GpuDisplay {
     fn as_raw_fd(&self) -> RawFd {
-        // Safe given that the context pointer is valid.
-        unsafe { dwl_context_fd(self.ctx.0) }
+        self.inner.as_raw_fd()
     }
 }