Skip to content

Add a function for retrieving the window contents #104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[alias]
run-wasm = ["run", "--release", "--package", "run-wasm", "--"]

[target.wasm32-unknown-unknown]
runner = "wasm-bindgen-test-runner"
31 changes: 20 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,10 @@ jobs:
- { target: x86_64-unknown-freebsd, os: ubuntu-latest, }
- { target: x86_64-unknown-netbsd, os: ubuntu-latest, }
- { target: x86_64-apple-darwin, os: macos-latest, }
# We're using Windows rather than Ubuntu to run the wasm tests because caching cargo-web
# doesn't currently work on Linux.
- { target: wasm32-unknown-unknown, os: windows-latest, }
- { target: wasm32-unknown-unknown, os: ubuntu-latest, }
include:
- rust_version: nightly
platform: { target: wasm32-unknown-unknown, os: windows-latest, options: "-Zbuild-std=panic_abort,std", rustflags: "-Ctarget-feature=+atomics,+bulk-memory" }
platform: { target: wasm32-unknown-unknown, os: ubuntu-latest, options: "-Zbuild-std=panic_abort,std", rustflags: "-Ctarget-feature=+atomics,+bulk-memory" }

env:
RUST_BACKTRACE: 1
Expand All @@ -67,12 +65,10 @@ jobs:
steps:
- uses: actions/checkout@v3

# Used to cache cargo-web
- name: Cache cargo folder
uses: actions/cache@v3
- uses: taiki-e/install-action@v2
if: matrix.platform.target == 'wasm32-unknown-unknown'
with:
path: ~/.cargo
key: ${{ matrix.platform.target }}-cargo-${{ matrix.rust_version }}
tool: wasm-bindgen-cli

- uses: hecrj/setup-rust-action@v1
with:
Expand Down Expand Up @@ -102,12 +98,25 @@ jobs:
shell: bash
if: >
!((matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')) &&
!contains(matrix.platform.target, 'wasm32') &&
!contains(matrix.platform.target, 'redox') &&
!contains(matrix.platform.target, 'freebsd') &&
!contains(matrix.platform.target, 'netbsd')
!contains(matrix.platform.target, 'netbsd') &&
!contains(matrix.platform.target, 'linux')
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES

# TODO: We should also be using Wayland for testing here.
- name: Run tests using Xvfb
shell: bash
if: >
!((matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')) &&
!contains(matrix.platform.target, 'redox') &&
!contains(matrix.platform.target, 'freebsd') &&
!contains(matrix.platform.target, 'netbsd') &&
contains(matrix.platform.target, 'linux') &&
!contains(matrix.platform.options, '--no-default-features') &&
!contains(matrix.platform.features, 'wayland')
run: xvfb-run cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES

- name: Lint with clippy
shell: bash
if: >
Expand Down
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ cfg_aliases = "0.1.1"
criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] }
instant = "0.1.12"
winit = "0.28.1"
winit-test = "0.1.0"

[dev-dependencies.image]
version = "0.24.6"
Expand All @@ -81,11 +82,19 @@ features = ["jpeg"]
image = "0.24.6"
rayon = "1.5.1"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3"

[workspace]
members = [
"run-wasm",
]

[[test]]
name = "present_and_fetch"
path = "tests/present_and_fetch.rs"
harness = false

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
Expand Down
5 changes: 5 additions & 0 deletions src/cg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,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.
Expand Down
21 changes: 21 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -306,6 +315,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.
Expand Down
5 changes: 5 additions & 0 deletions src/orbital.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,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 {
Expand Down
5 changes: 5 additions & 0 deletions src/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ impl WaylandImpl {
Ok(unsafe { buffer.buffers.as_mut().unwrap().1.mapped_mut() })
})?))
}

/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
}

pub struct BufferImpl<'a>(util::BorrowStack<'a, WaylandImpl, [u32]>);
Expand Down
32 changes: 29 additions & 3 deletions src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
}
Expand All @@ -44,6 +44,9 @@ pub struct WebImpl {

/// The current width of the canvas.
width: u32,

/// The current height of the canvas.
height: u32,
}

impl WebImpl {
Expand Down Expand Up @@ -76,6 +79,7 @@ impl WebImpl {
ctx,
buffer: Vec::new(),
width: 0,
height: 0,
})
}

Expand All @@ -92,18 +96,40 @@ impl WebImpl {
self.canvas.set_width(width);
self.canvas.set_height(height);
self.width = width;
self.height = height;
Ok(())
}

/// Get a pointer to the mutable buffer.
pub(crate) fn buffer_mut(&mut self) -> Result<BufferImpl, SoftBufferError> {
Ok(BufferImpl { imp: self })
}

/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
let image_data = self
.ctx
.get_image_data(0., 0., self.width.into(), self.height.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>;
}

Expand Down Expand Up @@ -171,7 +197,7 @@ impl<'a> BufferImpl<'a> {
let image_data = result.unwrap();

// This can only throw an error if `data` is detached, which is impossible.
self.imp.ctx.put_image_data(&image_data, 0.0, 0.0).unwrap();
self.imp.ctx.put_image_data(&image_data, 0., 0.).unwrap();

Ok(())
}
Expand Down
28 changes: 28 additions & 0 deletions src/win32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,34 @@ impl Win32Impl {

Ok(BufferImpl(self))
}

/// 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);
Expand Down
Loading