blob: 5e6b9410b91c9bbd1363b261c3735c92818325d7 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use failure::{format_err, Error};
use fidl_fuchsia_images as images;
use fuchsia_scenic as scenic;
use fuchsia_wayland_core as wl;
use std::rc::Rc;
use wayland::*;
use crate::client::Client;
use crate::object::{NewObjectExt, ObjectRef, RequestReceiver};
/// The set of pixel formats that will be announced to clients.
const SUPPORTED_PIXEL_FORMATS: &[wl_shm::Format] =
&[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888];
/// Converts a wayland pixel format to the Fuchsia counterpart.
///
/// Returns `None` if the format is not supported.
fn pixel_format_wl_to_fuchsia(wl_format: wl_shm::Format) -> Option<images::PixelFormat> {
match wl_format {
wl_shm::Format::Argb8888 | wl_shm::Format::Xrgb8888 => Some(images::PixelFormat::Bgra8),
_ => None,
}
}
/// The wl_shm global.
pub struct Shm {
scenic: scenic::SessionPtr,
}
impl Shm {
/// Creates a new `Shm`.
pub fn new(scenic: scenic::SessionPtr) -> Self {
Shm { scenic }
}
/// Posts an event back to the client for each supported SHM pixel format.
pub fn post_formats(&self, this: wl::ObjectId, client: &Client) -> Result<(), Error> {
for format in SUPPORTED_PIXEL_FORMATS.iter() {
client.post(this, WlShmEvent::Format { format: *format })?;
}
Ok(())
}
}
impl RequestReceiver<WlShm> for Shm {
fn receive(
this: ObjectRef<Self>, request: WlShmRequest, client: &mut Client,
) -> Result<(), Error> {
let WlShmRequest::CreatePool { id, fd, size } = request;
let scenic = this.get(client)?.scenic.clone();
let memory = scenic::Memory::new(
scenic.clone(),
fd.into(),
size as u64,
images::MemoryType::HostMemory,
);
id.implement(
client,
ShmPool {
memory: Rc::new(memory),
_size: size,
},
)?;
Ok(())
}
}
pub struct ShmPool {
/// The scenic memory object that wraps the VMO handle sent from the
/// client.
memory: Rc<scenic::Memory>,
/// The size of `memory`, in bytes.
_size: i32,
}
impl ShmPool {
/// Creates a new wl_buffer object backed by memory from this wl_shm_pool.
///
/// It's possible for the resultant wl_buffer to outlive this pool, so the
/// buffer must hold a strong reference to any resources it depends on
/// post-creation.
pub fn create_buffer(
this: ObjectRef<Self>, client: &mut Client, offset: u32, width: i32, height: i32,
stride: i32, format: wl_shm::Format,
) -> Result<Buffer, Error> {
// TODO(tjdetwiler): Support sending the protocol-modeled errors,
// ex: wl_shm::Error::InvalidFormat.
let format = pixel_format_wl_to_fuchsia(format)
.ok_or_else(|| format_err!("Invalid pixel format {:?}", format))?;
let image_info = images::ImageInfo {
width: width as u32,
height: height as u32,
stride: stride as u32,
pixel_format: format,
transform: images::Transform::Normal,
color_space: images::ColorSpace::Srgb,
tiling: images::Tiling::Linear,
alpha_format: images::AlphaFormat::Opaque,
};
let this = this.get(client)?;
Ok(Buffer {
memory: this.memory.clone(),
offset,
image_info,
})
}
}
impl RequestReceiver<WlShmPool> for ShmPool {
fn receive(
this: ObjectRef<Self>, request: WlShmPoolRequest, client: &mut Client,
) -> Result<(), Error> {
match request {
WlShmPoolRequest::Destroy => {
client.delete_id(this.id())?;
}
WlShmPoolRequest::CreateBuffer {
id,
offset,
width,
height,
stride,
format,
} => {
if offset < 0 {
return Err(format_err!(
"Negative buffer offset not supported: {}",
offset
));
}
let offset = offset as u32;
let buffer = Self::create_buffer(
this,
client,
offset,
width,
height,
stride,
format.as_enum()?,
)?;
id.implement(client, buffer)?;
}
WlShmPoolRequest::Resize { .. } => {}
}
Ok(())
}
}
/// An implementation of 'wl_buffer'.
pub struct Buffer {
/// The scenic `Memory` object that will back this buffer.
memory: Rc<scenic::Memory>,
/// The offest into `memory` that this image is located.
offset: u32,
/// Additional description of the format of the buffer needed to draw the
/// image correctly.
image_info: images::ImageInfo,
}
/// Allow this to be `Clone` so that surfaces can keep the underlying
/// `scenic::Memory` alive even if the buffer protocol object has been released.
impl Clone for Buffer {
fn clone(&self) -> Self {
Buffer {
memory: self.memory.clone(),
offset: self.offset,
image_info: images::ImageInfo { ..self.image_info },
}
}
}
impl Buffer {
pub fn image_info(&self) -> &images::ImageInfo {
&self.image_info
}
/// Create a new scenic `Image` for this buffer. This entity must be
/// recreated after each change to the backing `Memory` in order for scenic
/// to present any changes to the `Memory` since it was last presented.
pub fn create_image(&self) -> scenic::Image {
let image_info = images::ImageInfo { ..self.image_info };
scenic::Image::new(&*self.memory, self.offset, image_info)
}
}
impl RequestReceiver<WlBuffer> for Buffer {
fn receive(
this: ObjectRef<Self>, request: WlBufferRequest, client: &mut Client,
) -> Result<(), Error> {
let WlBufferRequest::Destroy = request;
client.delete_id(this.id())?;
Ok(())
}
}