Merge remote-tracking branch 'origin/master' into damage

This commit is contained in:
Ian Douglas Scott 2023-06-02 15:03:10 -07:00
commit 1e7b9213d2
13 changed files with 265 additions and 23 deletions

View file

@ -69,6 +69,11 @@ impl CGImpl {
imp: self,
})
}
/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
}
pub struct BufferImpl<'a> {

View file

@ -43,6 +43,9 @@ pub enum SoftBufferError {
#[error("Platform error")]
PlatformError(Option<String>, Option<Box<dyn Error>>),
#[error("This function is unimplemented on this platform")]
Unimplemented,
}
/// Convenient wrapper to cast errors into SoftBufferError.

View file

@ -98,6 +98,15 @@ macro_rules! make_dispatch {
)*
}
}
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.fetch(),
)*
}
}
}
enum BufferDispatch<'a> {
@ -337,6 +346,18 @@ impl Surface {
self.surface_impl.resize(width, height)
}
/// Copies the window contents into a buffer.
///
/// ## Platform Dependent Behavior
///
/// - On X11, the window must be visible.
/// - On macOS, Redox and Wayland, this function is unimplemented.
/// - On Web, this will fail if the content was supplied by
/// a different origin depending on the sites CORS rules.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
self.surface_impl.fetch()
}
/// Return a [`Buffer`] that the next frame should be rendered into. The size must
/// be set with [`Surface::resize`] first. The initial contents of the buffer may be zeroed, or
/// may contain a previous frame. Call [`Buffer::age`] to determine this.

View file

@ -150,6 +150,11 @@ impl OrbitalImpl {
// Tell orbital to show the latest window data
syscall::fsync(self.window_fd()).expect("failed to sync orbital window");
}
/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
}
enum Pixels {

View file

@ -126,7 +126,6 @@ impl WaylandImpl {
};
let age = self.buffers.as_mut().unwrap().1.age;
Ok(BufferImpl {
stack: util::BorrowStack::new(self, |buffer| {
Ok(unsafe { buffer.buffers.as_mut().unwrap().1.mapped_mut() })
@ -135,6 +134,11 @@ impl WaylandImpl {
})
}
/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> {
let _ = self
.display

View file

@ -24,9 +24,9 @@ pub struct WebDisplayImpl {
impl WebDisplayImpl {
pub(super) fn new() -> Result<Self, SoftBufferError> {
let document = web_sys::window()
.swbuf_err("`window` is not present in this runtime")?
.swbuf_err("`Window` is not present in this runtime")?
.document()
.swbuf_err("`document` is not present in this runtime")?;
.swbuf_err("`Document` is not present in this runtime")?;
Ok(Self { document })
}
@ -164,11 +164,36 @@ impl WebImpl {
Ok(())
}
/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
let (width, height) = self
.size
.expect("Must set size of surface before calling `fetch()`");
let image_data = self
.ctx
.get_image_data(0., 0., width.get().into(), height.get().into())
.ok()
// TODO: Can also error if width or height are 0.
.swbuf_err("`Canvas` contains pixels from a different origin")?;
Ok(image_data
.data()
.0
.chunks_exact(4)
.map(|chunk| u32::from_be_bytes([0, chunk[0], chunk[1], chunk[2]]))
.collect())
}
}
/// Extension methods for the Wasm target on [`Surface`](crate::Surface).
pub trait SurfaceExtWeb: Sized {
/// Creates a new instance of this struct, using the provided [`HtmlCanvasElement`].
///
/// # Errors
/// - If the canvas was already controlled by an `OffscreenCanvas`.
/// - If a another context then "2d" was already created for this canvas.
fn from_canvas(canvas: HtmlCanvasElement) -> Result<Self, SoftBufferError>;
}

View file

@ -228,6 +228,34 @@ impl Win32Impl {
Ok(())
}
/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
let buffer = self.buffer.as_ref().unwrap();
let temp_buffer = Buffer::new(self.dc, buffer.width, buffer.height);
// Just go the other way.
unsafe {
Gdi::BitBlt(
temp_buffer.dc,
0,
0,
temp_buffer.width.get(),
temp_buffer.height.get(),
self.dc,
0,
0,
Gdi::SRCCOPY,
);
}
// Flush the operation so that it happens immediately.
unsafe {
Gdi::GdiFlush();
}
Ok(temp_buffer.pixels().to_vec())
}
}
pub struct BufferImpl<'a>(&'a mut Win32Impl);

View file

@ -109,6 +109,9 @@ pub struct X11Impl {
/// The depth (bits per pixel) of the drawing context.
depth: u8,
/// The visual ID of the drawing context.
visual_id: u32,
/// The buffer we draw to.
buffer: Buffer,
@ -183,11 +186,26 @@ impl X11Impl {
let window = window_handle.window;
// Run in parallel: start getting the window depth.
let geometry_token = display
.connection
.get_geometry(window)
.swbuf_err("Failed to send geometry request")?;
// Run in parallel: start getting the window depth and (if necessary) visual.
let display2 = display.clone();
let tokens = {
let geometry_token = display2
.connection
.get_geometry(window)
.swbuf_err("Failed to send geometry request")?;
let window_attrs_token = if window_handle.visual_id == 0 {
Some(
display2
.connection
.get_window_attributes(window)
.swbuf_err("Failed to send window attributes request")?,
)
} else {
None
};
(geometry_token, window_attrs_token)
};
// Create a new graphics context to draw to.
let gc = display
@ -206,9 +224,23 @@ impl X11Impl {
.swbuf_err("Failed to create GC")?;
// Finish getting the depth of the window.
let geometry_reply = geometry_token
.reply()
.swbuf_err("Failed to get geometry reply")?;
let (geometry_reply, visual_id) = {
let (geometry_token, window_attrs_token) = tokens;
let geometry_reply = geometry_token
.reply()
.swbuf_err("Failed to get geometry reply")?;
let visual_id = match window_attrs_token {
None => window_handle.visual_id,
Some(window_attrs) => {
window_attrs
.reply()
.swbuf_err("Failed to get window attributes reply")?
.visual
}
};
(geometry_reply, visual_id)
};
// See if SHM is available.
let buffer = if display.is_shm_available {
@ -227,6 +259,7 @@ impl X11Impl {
window,
gc,
depth: geometry_reply.depth,
visual_id,
buffer,
buffer_presented: false,
size: None,
@ -278,6 +311,43 @@ impl X11Impl {
// We can now safely call `buffer_mut` on the buffer.
Ok(BufferImpl(self))
}
/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
log::trace!("fetch: window={:X}", self.window);
let (width, height) = self
.size
.expect("Must set size of surface before calling `fetch()`");
// TODO: Is it worth it to do SHM here? Probably not.
let reply = self
.display
.connection
.get_image(
xproto::ImageFormat::Z_PIXMAP,
self.window,
0,
0,
width.get(),
height.get(),
u32::MAX,
)
.swbuf_err("Failed to send image fetching request")?
.reply()
.swbuf_err("Failed to fetch image from window")?;
if reply.depth == self.depth && reply.visual == self.visual_id {
let mut out = vec![0u32; reply.data.len() / 4];
bytemuck::cast_slice_mut::<u32, u8>(&mut out).copy_from_slice(&reply.data);
Ok(out)
} else {
Err(SoftBufferError::PlatformError(
Some("Mismatch between reply and window data".into()),
None,
))
}
}
}
pub struct BufferImpl<'a>(&'a mut X11Impl);