diff options
author | Zach Reizner <zachr@google.com> | 2019-04-16 15:09:20 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-07-25 22:15:48 +0000 |
commit | f5285c647acacb4f25ef8cf9334254b976e71686 (patch) | |
tree | fcc238ec97736727a9c18b3b9de29be3dce3983e /gpu_display | |
parent | b2110bef59d72529d99c722df9b3e9a1d705e6f4 (diff) | |
download | crosvm-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.toml | 3 | ||||
-rw-r--r-- | gpu_display/examples/simple.rs | 4 | ||||
-rw-r--r-- | gpu_display/examples/simple_open.rs | 25 | ||||
-rw-r--r-- | gpu_display/src/generated/xlib.rs | 892 | ||||
-rwxr-xr-x | gpu_display/src/generated/xlib_generator.sh | 76 | ||||
-rw-r--r-- | gpu_display/src/generated/xlib_wrapper.h | 4 | ||||
-rw-r--r-- | gpu_display/src/gpu_display_wl.rs | 347 | ||||
-rw-r--r-- | gpu_display/src/gpu_display_x.rs | 647 | ||||
-rw-r--r-- | gpu_display/src/lib.rs | 404 |
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() } } |