diff --git a/CHANGELOG.md b/CHANGELOG.md index b1e1b15..4344cc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- **Breaking:** Use `u32` instead of `NonZeroU32`, and handle zero-sized buffers internally. + # 0.4.7 - Fix documentation building on `docs.rs`. diff --git a/Cargo.toml b/Cargo.toml index e294995..1ba236c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,8 +26,9 @@ wayland = [ "memmap2", "rustix", "fastrand", + "winit/wayland", ] -wayland-dlopen = ["wayland-sys/dlopen"] +wayland-dlopen = ["wayland-sys/dlopen", "winit/wayland-dlopen"] x11 = [ "as-raw-xcb-connection", "bytemuck", @@ -35,6 +36,7 @@ x11 = [ "rustix", "tiny-xlib", "x11rb", + "winit/x11", ] x11-dlopen = ["tiny-xlib/dlopen", "x11rb/dl-libxcb"] @@ -133,7 +135,10 @@ redox_syscall = "0.5" [dev-dependencies] colorous = "1.0.12" web-time = "1.0.0" -winit = "0.30.0" +winit = { version = "0.30.0", default-features = false, features = [ + "rwh_06", + "wayland-csd-adwaita", +] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } [target.'cfg(target_os = "android")'.dev-dependencies] diff --git a/README.md b/README.md index 2b1d4d8..e256ccd 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,6 @@ To run the Android-specific example on an Android phone: `cargo apk r --example ## Example ```rust,no_run -use std::num::NonZeroU32; use std::rc::Rc; use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; @@ -102,12 +101,7 @@ fn main() { return; }; let size = window.inner_size(); - surface - .resize( - NonZeroU32::new(size.width).unwrap(), - NonZeroU32::new(size.height).unwrap(), - ) - .unwrap(); + surface.resize(size.width, size.height).unwrap(); let mut buffer = surface.buffer_mut().unwrap(); for index in 0..(buffer.width().get() * buffer.height().get()) { diff --git a/benches/buffer_mut.rs b/benches/buffer_mut.rs index 5a0fd7e..24a1bc9 100644 --- a/benches/buffer_mut.rs +++ b/benches/buffer_mut.rs @@ -8,7 +8,6 @@ fn buffer_mut(c: &mut criterion::Criterion) { use criterion::black_box; use softbuffer::{Context, Surface}; - use std::num::NonZeroU32; use winit::event_loop::ControlFlow; use winit::platform::run_on_demand::EventLoopExtRunOnDemand; @@ -27,12 +26,7 @@ fn buffer_mut(c: &mut criterion::Criterion) { let mut surface = Surface::new(&context, &window).unwrap(); let size = window.inner_size(); - surface - .resize( - NonZeroU32::new(size.width).unwrap(), - NonZeroU32::new(size.height).unwrap(), - ) - .unwrap(); + surface.resize(size.width, size.height).unwrap(); c.bench_function("buffer_mut()", |b| { b.iter(|| { diff --git a/examples/animation.rs b/examples/animation.rs index 4a30856..04a6625 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -1,7 +1,6 @@ #[cfg(not(target_family = "wasm"))] use rayon::prelude::*; use std::f64::consts::PI; -use std::num::NonZeroU32; use web_time::Instant; use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; @@ -46,11 +45,7 @@ fn main() { return; }; - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - surface.resize(width, height).unwrap(); - } + surface.resize(size.width, size.height).unwrap(); } WindowEvent::RedrawRequested => { let Some(surface) = surface else { @@ -62,7 +57,7 @@ fn main() { let mut buffer = surface.buffer_mut().unwrap(); - let size = (buffer.width().get(), buffer.height().get()); + let size = (buffer.width(), buffer.height()); if size != *old_size { *old_size = size; *frames = pre_render_frames(size.0, size.1); diff --git a/examples/drm.rs b/examples/drm.rs index 78c0af5..4234dce 100644 --- a/examples/drm.rs +++ b/examples/drm.rs @@ -19,7 +19,6 @@ mod imple { use raw_window_handle::{DisplayHandle, DrmDisplayHandle, DrmWindowHandle, WindowHandle}; use softbuffer::{Context, Surface}; - use std::num::NonZeroU32; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd}; use std::path::Path; use std::time::{Duration, Instant}; @@ -114,10 +113,7 @@ mod imple { // Resize the surface. let (width, height) = mode.size(); - surface.resize( - NonZeroU32::new(width as u32).unwrap(), - NonZeroU32::new(height as u32).unwrap(), - )?; + surface.resize(width as u32, height as u32)?; // Start drawing to it. let start = Instant::now(); diff --git a/examples/fruit.rs b/examples/fruit.rs index 4f76b44..b8bdc2f 100644 --- a/examples/fruit.rs +++ b/examples/fruit.rs @@ -1,5 +1,4 @@ use image::GenericImageView; -use std::num::NonZeroU32; use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::{Key, NamedKey}; @@ -27,12 +26,7 @@ fn main() { // Intentionally only set the size of the surface once, at creation. // This is needed if the window chooses to ignore the size we passed in above, and for the // platforms softbuffer supports that don't yet extract the size from the window. - surface - .resize( - NonZeroU32::new(width).unwrap(), - NonZeroU32::new(height).unwrap(), - ) - .unwrap(); + surface.resize(width, height).unwrap(); surface }, ) diff --git a/examples/libxcb.rs b/examples/libxcb.rs index 1f9f8c9..e8a431c 100644 --- a/examples/libxcb.rs +++ b/examples/libxcb.rs @@ -117,12 +117,7 @@ mod example { match event { Event::Expose(_) => { // Draw a width x height red rectangle. - surface - .resize( - NonZeroU32::new(width.into()).unwrap(), - NonZeroU32::new(height.into()).unwrap(), - ) - .unwrap(); + surface.resize(width.into(), height.into()).unwrap(); let mut buffer = surface.buffer_mut().unwrap(); buffer.fill(RED); buffer.present().unwrap(); diff --git a/examples/rectangle.rs b/examples/rectangle.rs index e7e5346..26b6777 100644 --- a/examples/rectangle.rs +++ b/examples/rectangle.rs @@ -1,6 +1,5 @@ use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use softbuffer::Buffer; -use std::num::NonZeroU32; use winit::event::{ElementState, KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::{Key, NamedKey}; @@ -8,8 +7,8 @@ use winit::keyboard::{Key, NamedKey}; mod util; fn redraw(buffer: &mut Buffer<'_, impl HasDisplayHandle, impl HasWindowHandle>, flag: bool) { - let width = buffer.width().get(); - let height = buffer.height().get(); + let width = buffer.width(); + let height = buffer.height(); for y in 0..height { for x in 0..width { let value = if flag && x >= 100 && x < width - 100 && y >= 100 && y < height - 100 { @@ -59,12 +58,8 @@ fn main() { return; }; - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - // Resize surface - surface.resize(width, height).unwrap(); - } + // Resize surface + surface.resize(size.width, size.height).unwrap(); } WindowEvent::RedrawRequested => { let Some(surface) = surface else { diff --git a/examples/winit.rs b/examples/winit.rs index 330860d..b3adc12 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -1,4 +1,3 @@ -use std::num::NonZeroU32; use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::{Key, NamedKey}; @@ -33,11 +32,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { return; }; - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - surface.resize(width, height).unwrap(); - } + surface.resize(size.width, size.height).unwrap(); } WindowEvent::RedrawRequested => { let Some(surface) = surface else { @@ -46,12 +41,12 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { }; let mut buffer = surface.buffer_mut().unwrap(); - for y in 0..buffer.height().get() { - for x in 0..buffer.width().get() { + for y in 0..buffer.height() { + for x in 0..buffer.width() { let red = x % 255; let green = y % 255; let blue = (x * y) % 255; - let index = y * buffer.width().get() + x; + let index = y * buffer.width() + x; buffer[index as usize] = blue | (green << 8) | (red << 16); } } diff --git a/examples/winit_multithread.rs b/examples/winit_multithread.rs index 369eeaa..61cb165 100644 --- a/examples/winit_multithread.rs +++ b/examples/winit_multithread.rs @@ -4,8 +4,8 @@ mod util; #[cfg(not(target_family = "wasm"))] pub mod ex { - use std::num::NonZeroU32; use std::sync::{mpsc, Arc, Mutex}; + use winit::dpi::PhysicalSize; use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop, OwnedDisplayHandle}; use winit::keyboard::{Key, NamedKey}; @@ -16,12 +16,12 @@ pub mod ex { type Surface = softbuffer::Surface>; fn render_thread( - do_render: mpsc::Receiver<(Arc>, NonZeroU32, NonZeroU32)>, + do_render: mpsc::Receiver<(Arc>, PhysicalSize)>, done: mpsc::Sender<()>, ) { loop { tracing::info!("waiting for render..."); - let Ok((surface, width, height)) = do_render.recv() else { + let Ok((surface, size)) = do_render.recv() else { tracing::info!("main thread destroyed"); break; }; @@ -29,15 +29,15 @@ pub mod ex { // Perform the rendering. let mut surface = surface.lock().unwrap(); tracing::info!("resizing..."); - surface.resize(width, height).unwrap(); + surface.resize(size.width, size.height).unwrap(); let mut buffer = surface.buffer_mut().unwrap(); - for y in 0..buffer.height().get() { - for x in 0..buffer.width().get() { + for y in 0..buffer.height() { + for x in 0..buffer.width() { let red = x % 255; let green = y % 255; let blue = (x * y) % 255; - let index = y * buffer.width().get() + x; + let index = y * buffer.width() + x; buffer[index as usize] = blue | (green << 8) | (red << 16); } } @@ -96,13 +96,9 @@ pub mod ex { let size = window.inner_size(); tracing::info!("got size: {size:?}"); - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - // Start the render and then finish it. - start_render.send((surface.clone(), width, height)).unwrap(); - finish_render.recv().unwrap(); - } + // Start the render and then finish it. + start_render.send((surface.clone(), size)).unwrap(); + finish_render.recv().unwrap(); } WindowEvent::CloseRequested | WindowEvent::KeyboardInput { diff --git a/examples/winit_wrong_sized_buffer.rs b/examples/winit_wrong_sized_buffer.rs index 22b2547..e544789 100644 --- a/examples/winit_wrong_sized_buffer.rs +++ b/examples/winit_wrong_sized_buffer.rs @@ -1,4 +1,7 @@ -use std::num::NonZeroU32; +//! A window with a surface that is 200 pixels less wide and 400 pixels less tall. +//! +//! This is useful for testing that zero-sized buffers work, as well as testing buffer vs. window +//! size discrepancies in general. use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::{Key, NamedKey}; @@ -14,11 +17,14 @@ fn main() { let app = util::WinitAppBuilder::with_init( |elwt| util::make_window(elwt, |w| w), move |_elwt, window| { + let size = window.inner_size(); + let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); - // Intentionally set the size of the surface to something different than the size of the window. - surface - .resize(NonZeroU32::new(256).unwrap(), NonZeroU32::new(128).unwrap()) - .unwrap(); + + let width = size.width.saturating_sub(200); + let height = size.height.saturating_sub(400); + tracing::info!("size initially at: {width}/{height}"); + surface.resize(width, height).unwrap(); surface }, ) @@ -30,6 +36,17 @@ fn main() { } match event { + WindowEvent::Resized(size) => { + let Some(surface) = surface else { + tracing::warn!("RedrawRequested fired before Resumed or after Suspended"); + return; + }; + + let width = size.width.saturating_sub(200); + let height = size.height.saturating_sub(400); + tracing::info!("resized to: {width}/{height}"); + surface.resize(width, height).unwrap(); + } WindowEvent::RedrawRequested => { let Some(surface) = surface else { tracing::warn!("RedrawRequested fired before Resumed or after Suspended"); @@ -37,17 +54,16 @@ fn main() { }; let mut buffer = surface.buffer_mut().unwrap(); - let width = buffer.width().get(); - for y in 0..buffer.height().get() { - for x in 0..width { + for y in 0..buffer.height() { + for x in 0..buffer.width() { let red = x % 255; let green = y % 255; let blue = (x * y) % 255; - - let color = blue | (green << 8) | (red << 16); - buffer[(y * width + x) as usize] = color; + let index = y * buffer.width() + x; + buffer[index as usize] = blue | (green << 8) | (red << 16); } } + buffer.present().unwrap(); } WindowEvent::CloseRequested diff --git a/src/backend_dispatch.rs b/src/backend_dispatch.rs index 3bfe94b..d4649d9 100644 --- a/src/backend_dispatch.rs +++ b/src/backend_dispatch.rs @@ -4,7 +4,6 @@ use crate::{backend_interface::*, backends, InitError, Rect, SoftBufferError}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use std::fmt; -use std::num::NonZeroU32; /// A macro for creating the enum used to statically dispatch to the platform-specific implementation. macro_rules! make_dispatch { @@ -99,7 +98,7 @@ macro_rules! make_dispatch { } } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { match self { $( $(#[$attr])* @@ -147,7 +146,7 @@ macro_rules! make_dispatch { impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferDispatch<'a, D, W> { #[inline] - fn width(&self) -> NonZeroU32 { + fn width(&self) -> u32 { match self { $( $(#[$attr])* @@ -157,7 +156,7 @@ macro_rules! make_dispatch { } #[inline] - fn height(&self) -> NonZeroU32 { + fn height(&self) -> u32 { match self { $( $(#[$attr])* diff --git a/src/backend_interface.rs b/src/backend_interface.rs index b6a5666..35416fe 100644 --- a/src/backend_interface.rs +++ b/src/backend_interface.rs @@ -3,7 +3,6 @@ use crate::{InitError, Rect, SoftBufferError}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use std::num::NonZeroU32; pub(crate) trait ContextInterface { fn new(display: D) -> Result> @@ -25,7 +24,7 @@ pub(crate) trait SurfaceInterface &W; /// Resize the internal buffer to the given width and height. - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError>; + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError>; /// Get a mutable reference to the buffer. fn buffer_mut(&mut self) -> Result, SoftBufferError>; /// Fetch the buffer from the window. @@ -35,8 +34,8 @@ pub(crate) trait SurfaceInterface NonZeroU32; - fn height(&self) -> NonZeroU32; + fn width(&self) -> u32; + fn height(&self) -> u32; fn pixels(&self) -> &[u32]; fn pixels_mut(&mut self) -> &mut [u32]; fn age(&self) -> u8; diff --git a/src/backends/android.rs b/src/backends/android.rs index 12d489b..c21a463 100644 --- a/src/backends/android.rs +++ b/src/backends/android.rs @@ -1,7 +1,6 @@ //! Implementation of software buffering for Android. use std::marker::PhantomData; -use std::num::{NonZeroI32, NonZeroU32}; use ndk::{ hardware_buffer_format::HardwareBufferFormat, @@ -18,6 +17,8 @@ use crate::{util, BufferInterface, Rect, SoftBufferError, SurfaceInterface}; #[derive(Debug)] pub struct AndroidImpl { native_window: NativeWindow, + width: u32, + height: u32, window: W, _display: PhantomData, } @@ -42,6 +43,8 @@ impl SurfaceInterface for Android Ok(Self { native_window, + width: 0, + height: 0, _display: PhantomData, window, }) @@ -53,18 +56,15 @@ impl SurfaceInterface for Android } /// Also changes the pixel format to [`HardwareBufferFormat::R8G8B8A8_UNORM`]. - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - let (width, height) = (|| { - let width = NonZeroI32::try_from(width).ok()?; - let height = NonZeroI32::try_from(height).ok()?; - Some((width, height)) - })() - .ok_or(SoftBufferError::SizeOutOfRange { width, height })?; + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { + let (width_i32, height_i32) = util::convert_size::(width, height) + .map_err(|_| SoftBufferError::SizeOutOfRange { width, height })?; + // Make the Window's buffer be at least 1 pixel wide/high. self.native_window .set_buffers_geometry( - width.into(), - height.into(), + width_i32.max(1), + height_i32.max(1), // Default is typically R5G6B5 16bpp, switch to 32bpp Some(HardwareBufferFormat::R8G8B8X8_UNORM), ) @@ -73,7 +73,11 @@ impl SurfaceInterface for Android Some("Failed to set buffer geometry on ANativeWindow".to_owned()), Some(Box::new(err)), ) - }) + })?; + self.width = width; + self.height = height; + + Ok(()) } fn buffer_mut(&mut self) -> Result, SoftBufferError> { @@ -99,10 +103,12 @@ impl SurfaceInterface for Android )); } - let buffer = vec![0; native_window_buffer.width() * native_window_buffer.height()]; + let buffer = vec![0; self.width as usize * self.height as usize]; Ok(BufferImpl { native_window_buffer, + width: self.width, + height: self.height, buffer: util::PixelBuffer(buffer), marker: PhantomData, }) @@ -117,6 +123,8 @@ impl SurfaceInterface for Android #[derive(Debug)] pub struct BufferImpl<'a, D: ?Sized, W> { native_window_buffer: NativeWindowBufferLockGuard<'a>, + width: u32, + height: u32, buffer: util::PixelBuffer, marker: PhantomData<(&'a D, &'a W)>, } @@ -125,12 +133,12 @@ pub struct BufferImpl<'a, D: ?Sized, W> { unsafe impl<'a, D, W> Send for BufferImpl<'a, D, W> {} impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W> { - fn width(&self) -> NonZeroU32 { - NonZeroU32::new(self.native_window_buffer.width() as u32).unwrap() + fn width(&self) -> u32 { + self.width } - fn height(&self) -> NonZeroU32 { - NonZeroU32::new(self.native_window_buffer.height() as u32).unwrap() + fn height(&self) -> u32 { + self.height } #[inline] @@ -150,7 +158,7 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl // TODO: This function is pretty slow this way fn present(mut self) -> Result<(), SoftBufferError> { - let input_lines = self.buffer.chunks(self.native_window_buffer.width()); + let input_lines = self.buffer.chunks(self.width as usize); for (output, input) in self .native_window_buffer .lines() diff --git a/src/backends/cg.rs b/src/backends/cg.rs index a5dc52a..8bdba5e 100644 --- a/src/backends/cg.rs +++ b/src/backends/cg.rs @@ -21,7 +21,6 @@ use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; use std::ffi::c_void; use std::marker::PhantomData; use std::mem::size_of; -use std::num::NonZeroU32; use std::ops::Deref; use std::ptr::{self, slice_from_raw_parts_mut, NonNull}; @@ -105,9 +104,9 @@ pub struct CGImpl { observer: Retained, color_space: CFRetained, /// The width of the underlying buffer. - width: usize, + width: u32, /// The height of the underlying buffer. - height: usize, + height: u32, window_handle: W, _display: PhantomData, } @@ -228,20 +227,13 @@ impl SurfaceInterface for CGImpl< // Initialize color space here, to reduce work later on. let color_space = CGColorSpace::new_device_rgb().unwrap(); - // Grab initial width and height from the layer (whose properties have just been initialized - // by the observer using `NSKeyValueObservingOptionInitial`). - let size = layer.bounds().size; - let scale_factor = layer.contentsScale(); - let width = (size.width * scale_factor) as usize; - let height = (size.height * scale_factor) as usize; - Ok(Self { layer: SendCALayer(layer), root_layer: SendCALayer(root_layer), observer, color_space, - width, - height, + width: 0, + height: 0, _display: PhantomData, window_handle: window_src, }) @@ -252,15 +244,15 @@ impl SurfaceInterface for CGImpl< &self.window_handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - self.width = width.get() as usize; - self.height = height.get() as usize; + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { + self.width = width; + self.height = height; Ok(()) } fn buffer_mut(&mut self) -> Result, SoftBufferError> { Ok(BufferImpl { - buffer: util::PixelBuffer(vec![0; self.width * self.height]), + buffer: util::PixelBuffer(vec![0; self.width as usize * self.height as usize]), imp: self, }) } @@ -273,12 +265,12 @@ pub struct BufferImpl<'a, D, W> { } impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - NonZeroU32::new(self.imp.width as u32).unwrap() + fn width(&self) -> u32 { + self.imp.width } - fn height(&self) -> NonZeroU32 { - NonZeroU32::new(self.imp.height as u32).unwrap() + fn height(&self) -> u32 { + self.imp.height } #[inline] @@ -296,57 +288,65 @@ impl BufferInterface for BufferImpl<'_, } fn present(self) -> Result<(), SoftBufferError> { - unsafe extern "C-unwind" fn release( - _info: *mut c_void, - data: NonNull, - size: usize, - ) { - let data = data.cast::(); - let slice = slice_from_raw_parts_mut(data.as_ptr(), size / size_of::()); - // SAFETY: This is the same slice that we passed to `Box::into_raw` below. - drop(unsafe { Box::from_raw(slice) }) - } + let image = if !self.buffer.is_empty() { + unsafe extern "C-unwind" fn release( + _info: *mut c_void, + data: NonNull, + size: usize, + ) { + let data = data.cast::(); + let slice = slice_from_raw_parts_mut(data.as_ptr(), size / size_of::()); + // SAFETY: This is the same slice that we passed to `Box::into_raw` below. + drop(unsafe { Box::from_raw(slice) }) + } - let data_provider = { - let len = self.buffer.len() * size_of::(); - let buffer: *mut [u32] = Box::into_raw(self.buffer.0.into_boxed_slice()); - // Convert slice pointer to thin pointer. - let data_ptr = buffer.cast::(); + let data_provider = { + let len = self.buffer.len() * size_of::(); + let buffer: *mut [u32] = Box::into_raw(self.buffer.0.into_boxed_slice()); + // Convert slice pointer to thin pointer. + let data_ptr = buffer.cast::(); + + // SAFETY: The data pointer and length are valid. + // The info pointer can safely be NULL, we don't use it in the `release` callback. + unsafe { + CGDataProvider::with_data(ptr::null_mut(), data_ptr, len, Some(release)) + .unwrap() + } + }; + + // `CGBitmapInfo` consists of a combination of `CGImageAlphaInfo`, `CGImageComponentInfo` + // `CGImageByteOrderInfo` and `CGImagePixelFormatInfo` (see e.g. `CGBitmapInfoMake`). + // + // TODO: Use `CGBitmapInfo::new` once the next version of objc2-core-graphics is released. + let bitmap_info = CGBitmapInfo( + CGImageAlphaInfo::NoneSkipFirst.0 + | CGImageComponentInfo::Integer.0 + | CGImageByteOrderInfo::Order32Little.0 + | CGImagePixelFormatInfo::Packed.0, + ); - // SAFETY: The data pointer and length are valid. - // The info pointer can safely be NULL, we don't use it in the `release` callback. - unsafe { - CGDataProvider::with_data(ptr::null_mut(), data_ptr, len, Some(release)).unwrap() + let image = unsafe { + CGImage::new( + self.imp.width as usize, + self.imp.height as usize, + 8, + 32, + self.imp.width as usize * 4, + Some(&self.imp.color_space), + bitmap_info, + Some(&data_provider), + ptr::null(), + false, + CGColorRenderingIntent::RenderingIntentDefault, + ) } - }; + .unwrap(); - // `CGBitmapInfo` consists of a combination of `CGImageAlphaInfo`, `CGImageComponentInfo` - // `CGImageByteOrderInfo` and `CGImagePixelFormatInfo` (see e.g. `CGBitmapInfoMake`). - // - // TODO: Use `CGBitmapInfo::new` once the next version of objc2-core-graphics is released. - let bitmap_info = CGBitmapInfo( - CGImageAlphaInfo::NoneSkipFirst.0 - | CGImageComponentInfo::Integer.0 - | CGImageByteOrderInfo::Order32Little.0 - | CGImagePixelFormatInfo::Packed.0, - ); - - let image = unsafe { - CGImage::new( - self.imp.width, - self.imp.height, - 8, - 32, - self.imp.width * 4, - Some(&self.imp.color_space), - bitmap_info, - Some(&data_provider), - ptr::null(), - false, - CGColorRenderingIntent::RenderingIntentDefault, - ) - } - .unwrap(); + Some(image) + } else { + // Buffer is empty -> clear contents. + None + }; // The CALayer has a default action associated with a change in the layer contents, causing // a quarter second fade transition to happen every time a new buffer is applied. This can @@ -354,8 +354,9 @@ impl BufferInterface for BufferImpl<'_, CATransaction::begin(); CATransaction::setDisableActions(true); + let contents = image.as_ref().map(|i| i.as_ref()); // SAFETY: The contents is `CGImage`, which is a valid class for `contents`. - unsafe { self.imp.layer.setContents(Some(image.as_ref())) }; + unsafe { self.imp.layer.setContents(contents) }; CATransaction::commit(); Ok(()) diff --git a/src/backends/kms.rs b/src/backends/kms.rs index d18d633..675140e 100644 --- a/src/backends/kms.rs +++ b/src/backends/kms.rs @@ -103,8 +103,11 @@ pub(crate) struct BufferImpl<'a, D: ?Sized, W: ?Sized> { /// This is used to change the front buffer. first_is_front: &'a mut bool, - /// The current size. - size: (NonZeroU32, NonZeroU32), + /// The current width. + width: u32, + + /// The current height. + height: u32, /// The display implementation. display: &'a KmsDisplayImpl, @@ -219,7 +222,7 @@ impl SurfaceInterface fo &self.window_handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { // Don't resize if we don't have to. if let Some(buffer) = &self.buffer { let (buffer_width, buffer_height) = buffer.size(); @@ -273,7 +276,8 @@ impl SurfaceInterface fo Ok(BufferImpl { mapping, - size, + width: size.0, + height: size.1, first_is_front: &mut set.first_is_front, front_fb, crtc_handle: self.crtc.handle(), @@ -301,12 +305,12 @@ impl Drop for KmsImpl { } impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - self.size.0 + fn width(&self) -> u32 { + self.width } - fn height(&self) -> NonZeroU32 { - self.size.1 + fn height(&self) -> u32 { + self.height } #[inline] @@ -334,11 +338,11 @@ impl BufferInterface for BufferImpl<'_, D, W> { rect.x.try_into().map_err(|_| err())?, rect.y.try_into().map_err(|_| err())?, rect.x - .checked_add(rect.width.get()) + .checked_add(rect.width) .and_then(|x| x.try_into().ok()) .ok_or_else(err)?, rect.y - .checked_add(rect.height.get()) + .checked_add(rect.height) .and_then(|y| y.try_into().ok()) .ok_or_else(err)?, )) @@ -382,13 +386,13 @@ impl BufferInterface for BufferImpl<'_, D, W> { #[inline] fn present(self) -> Result<(), SoftBufferError> { - let (width, height) = self.size; - self.present_with_damage(&[crate::Rect { + let rect = crate::Rect { x: 0, y: 0, - width, - height, - }]) + width: self.width, + height: self.height, + }; + self.present_with_damage(&[rect]) } } @@ -396,11 +400,11 @@ impl SharedBuffer { /// Create a new buffer set. pub(crate) fn new( display: &KmsDisplayImpl, - width: NonZeroU32, - height: NonZeroU32, + width: u32, + height: u32, ) -> Result { let db = display - .create_dumb_buffer((width.get(), height.get()), DrmFourcc::Xrgb8888, 32) + .create_dumb_buffer((width, height), DrmFourcc::Xrgb8888, 32) .swbuf_err("failed to create dumb buffer")?; let fb = display .add_framebuffer(&db, 24, 32) @@ -408,20 +412,11 @@ impl SharedBuffer { Ok(SharedBuffer { fb, db, age: 0 }) } - - /// Get the size of this buffer. - pub(crate) fn size(&self) -> (NonZeroU32, NonZeroU32) { - let (width, height) = self.db.size(); - - NonZeroU32::new(width) - .and_then(|width| NonZeroU32::new(height).map(|height| (width, height))) - .expect("buffer size is zero") - } } impl Buffers { /// Get the size of this buffer. - pub(crate) fn size(&self) -> (NonZeroU32, NonZeroU32) { - self.buffers[0].size() + pub(crate) fn size(&self) -> (u32, u32) { + self.buffers[0].db.size() } } diff --git a/src/backends/orbital.rs b/src/backends/orbital.rs index 301baba..bb8836b 100644 --- a/src/backends/orbital.rs +++ b/src/backends/orbital.rs @@ -1,6 +1,6 @@ use crate::error::InitError; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, OrbitalWindowHandle, RawWindowHandle}; -use std::{cmp, marker::PhantomData, num::NonZeroU32, slice, str}; +use std::{cmp, marker::PhantomData, slice, str}; use crate::backend_interface::*; use crate::{util, Rect, SoftBufferError}; @@ -157,9 +157,7 @@ impl SurfaceInterface for Orbital &self.window_handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - let width = width.get(); - let height = height.get(); + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { if width != self.width || height != self.height { self.presented = false; self.width = width; @@ -200,12 +198,12 @@ pub struct BufferImpl<'a, D, W> { } impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - NonZeroU32::new(self.imp.width as u32).unwrap() + fn width(&self) -> u32 { + self.imp.width as u32 } - fn height(&self) -> NonZeroU32 { - NonZeroU32::new(self.imp.height as u32).unwrap() + fn height(&self) -> u32 { + self.imp.height as u32 } #[inline] diff --git a/src/backends/wayland/buffer.rs b/src/backends/wayland/buffer.rs index 49e641f..e2e5641 100644 --- a/src/backends/wayland/buffer.rs +++ b/src/backends/wayland/buffer.rs @@ -125,7 +125,7 @@ impl WaylandBuffer { self.buffer.destroy(); // Grow pool, if needed - let size = ((width * height * 4) as u32).next_power_of_two() as i32; + let size = get_pool_size(width, height); if size > self.pool_size { let _ = self.tempfile.set_len(size as u64); self.pool.resize(size); diff --git a/src/backends/wayland/mod.rs b/src/backends/wayland/mod.rs index 2d37494..cd0fd1c 100644 --- a/src/backends/wayland/mod.rs +++ b/src/backends/wayland/mod.rs @@ -4,10 +4,7 @@ use crate::{ util, Rect, SoftBufferError, }; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle}; -use std::{ - num::{NonZeroI32, NonZeroU32}, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; use wayland_client::{ backend::{Backend, ObjectId}, globals::{registry_queue_init, GlobalListContents}, @@ -80,7 +77,8 @@ pub struct WaylandImpl { display: Arc>, surface: Option, buffers: Option<(WaylandBuffer, WaylandBuffer)>, - size: Option<(NonZeroI32, NonZeroI32)>, + width: i32, + height: i32, /// The pointer to the window object. /// @@ -125,8 +123,8 @@ impl WaylandImpl { Some(( i32::try_from(rect.x).ok()?, i32::try_from(rect.y).ok()?, - i32::try_from(rect.width.get()).ok()?, - i32::try_from(rect.height.get()).ok()?, + i32::try_from(rect.width).ok()?, + i32::try_from(rect.height).ok()?, )) })() .ok_or(SoftBufferError::DamageOutOfRange { rect: *rect })?; @@ -177,7 +175,8 @@ impl SurfaceInterface display: display.clone(), surface: Some(surface), buffers: Default::default(), - size: None, + width: 0, + height: 0, window_handle: window, }) } @@ -187,23 +186,13 @@ impl SurfaceInterface &self.window_handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - self.size = Some( - (|| { - let width = NonZeroI32::try_from(width).ok()?; - let height = NonZeroI32::try_from(height).ok()?; - Some((width, height)) - })() - .ok_or(SoftBufferError::SizeOutOfRange { width, height })?, - ); + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { + (self.width, self.height) = util::convert_size::(width, height) + .map_err(|_| SoftBufferError::SizeOutOfRange { width, height })?; Ok(()) } fn buffer_mut(&mut self) -> Result, SoftBufferError> { - let (width, height) = self - .size - .expect("Must set size of surface before calling `buffer_mut()`"); - if let Some((_front, back)) = &mut self.buffers { // Block if back buffer not released yet if !back.released() { @@ -222,28 +211,26 @@ impl SurfaceInterface } } - // Resize, if buffer isn't large enough - back.resize(width.get(), height.get()); + // Buffers must be at least 1x1. + if self.width != 0 && self.height != 0 { + // Resize (if buffer isn't large enough). + back.resize(self.width, self.height); + } else { + // If we got `width == 0 || height == 0`, use a 1x1 buffer. + // + // Note that we can't just call `surface.attach(None)` here, since TODO. + back.resize(1, 1); + } } else { // Allocate front and back buffer self.buffers = Some(( - WaylandBuffer::new( - &self.display.shm, - width.get(), - height.get(), - &self.display.qh, - ), - WaylandBuffer::new( - &self.display.shm, - width.get(), - height.get(), - &self.display.qh, - ), + WaylandBuffer::new(&self.display.shm, self.width, self.height, &self.display.qh), + WaylandBuffer::new(&self.display.shm, self.width, self.height, &self.display.qh), )); }; - let width = self.buffers.as_mut().unwrap().1.width; - let height = self.buffers.as_mut().unwrap().1.height; + let width = self.width; + let height = self.height; let age = self.buffers.as_mut().unwrap().1.age; Ok(BufferImpl { stack: util::BorrowStack::new(self, |buffer| { @@ -272,22 +259,30 @@ pub struct BufferImpl<'a, D: ?Sized, W> { } impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - NonZeroU32::new(self.width as u32).unwrap() + fn width(&self) -> u32 { + self.width as u32 } - fn height(&self) -> NonZeroU32 { - NonZeroU32::new(self.height as usize as u32).unwrap() + fn height(&self) -> u32 { + self.height as u32 } #[inline] fn pixels(&self) -> &[u32] { - self.stack.member() + if self.width != 0 && self.height != 0 { + self.stack.member() + } else { + &[] + } } #[inline] fn pixels_mut(&mut self) -> &mut [u32] { - self.stack.member_mut() + if self.width != 0 && self.height != 0 { + self.stack.member_mut() + } else { + &mut [] + } } fn age(&self) -> u8 { @@ -300,15 +295,12 @@ impl BufferInterface for Buffe fn present(self) -> Result<(), SoftBufferError> { let imp = self.stack.into_container(); - let (width, height) = imp - .size - .expect("Must set size of surface before calling `present()`"); + // Damage the entire region. imp.present_with_damage(&[Rect { x: 0, y: 0, - // We know width/height will be non-negative - width: width.try_into().unwrap(), - height: height.try_into().unwrap(), + width: i32::MAX as u32, + height: i32::MAX as u32, }]) } } diff --git a/src/backends/web.rs b/src/backends/web.rs index 358e934..399a710 100644 --- a/src/backends/web.rs +++ b/src/backends/web.rs @@ -13,7 +13,6 @@ use crate::backend_interface::*; use crate::error::{InitError, SwResultExt}; use crate::{util, NoDisplayHandle, NoWindowHandle, Rect, SoftBufferError}; use std::marker::PhantomData; -use std::num::NonZeroU32; /// Display implementation for the web platform. /// @@ -54,8 +53,11 @@ pub struct WebImpl { /// Buffer has been presented. buffer_presented: bool, - /// The current canvas width/height. - size: Option<(NonZeroU32, NonZeroU32)>, + /// The current canvas width. + width: u32, + + /// The current canvas height. + height: u32, /// The underlying window handle. window_handle: W, @@ -86,7 +88,8 @@ impl WebImpl { canvas: Canvas::Canvas { canvas, ctx }, buffer: util::PixelBuffer(Vec::new()), buffer_presented: false, - size: None, + width: 0, + height: 0, window_handle: window, _display: PhantomData, }) @@ -102,7 +105,8 @@ impl WebImpl { canvas: Canvas::OffscreenCanvas { canvas, ctx }, buffer: util::PixelBuffer(Vec::new()), buffer_presented: false, - size: None, + width: 0, + height: 0, window_handle: window, _display: PhantomData, }) @@ -124,26 +128,35 @@ impl WebImpl { } fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { - let (buffer_width, _buffer_height) = self - .size - .expect("Must set size of surface before calling `present_with_damage()`"); - let union_damage = if let Some(rect) = util::union_damage(damage) { rect } else { + self.buffer_presented = true; return Ok(()); }; + if self.buffer.0.is_empty() { + self.canvas.clear_rect( + union_damage.x.into(), + union_damage.y.into(), + union_damage.width.into(), + union_damage.height.into(), + ); + self.buffer_presented = true; + return Ok(()); + } + // Create a bitmap from the buffer. let bitmap: Vec<_> = self .buffer - .chunks_exact(buffer_width.get() as usize) + .0 + .chunks_exact(self.width as usize) .skip(union_damage.y as usize) - .take(union_damage.height.get() as usize) + .take(union_damage.height as usize) .flat_map(|row| { row.iter() .skip(union_damage.x as usize) - .take(union_damage.width.get() as usize) + .take(union_damage.width as usize) }) .copied() .flat_map(|pixel| [(pixel >> 16) as u8, (pixel >> 8) as u8, pixel as u8, 255]) @@ -151,7 +164,7 @@ impl WebImpl { debug_assert_eq!( bitmap.len() as u32, - union_damage.width.get() * union_damage.height.get() * 4 + union_damage.width * union_damage.height * 4 ); #[cfg(target_feature = "atomics")] @@ -174,14 +187,14 @@ impl WebImpl { let array = Uint8Array::new_with_length(bitmap.len() as u32); array.copy_from(&bitmap); let array = Uint8ClampedArray::new(&array); - ImageDataExt::new(array, union_damage.width.get()) + ImageDataExt::new(array, union_damage.width) .map(JsValue::from) .map(ImageData::unchecked_from_js) }; #[cfg(not(target_feature = "atomics"))] let result = ImageData::new_with_u8_clamped_array( wasm_bindgen::Clamped(&bitmap), - union_damage.width.get(), + union_damage.width, ); // This should only throw an error if the buffer we pass's size is incorrect. let image_data = result.unwrap(); @@ -195,8 +208,8 @@ impl WebImpl { union_damage.y.into(), (rect.x - union_damage.x).into(), (rect.y - union_damage.y).into(), - rect.width.get().into(), - rect.height.get().into(), + rect.width.into(), + rect.height.into(), ) .unwrap(); } @@ -251,13 +264,14 @@ impl SurfaceInterface for WebImpl /// De-duplicates the error handling between `HtmlCanvasElement` and `OffscreenCanvas`. /// Resize the canvas to the given dimensions. - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - if self.size != Some((width, height)) { + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { + if self.width != width && self.height != height { self.buffer_presented = false; - self.buffer.resize(total_len(width.get(), height.get()), 0); - self.canvas.set_width(width.get()); - self.canvas.set_height(height.get()); - self.size = Some((width, height)); + self.buffer.resize(total_len(width, height), 0); + self.canvas.set_width(width); + self.canvas.set_height(height); + self.width = width; + self.height = height; } Ok(()) @@ -268,13 +282,14 @@ impl SurfaceInterface for WebImpl } fn fetch(&mut self) -> Result, SoftBufferError> { - let (width, height) = self - .size - .expect("Must set size of surface before calling `fetch()`"); + if self.width == 0 || self.height == 0 { + // `getImageData` throws an `IndexSizeError` if the width or height is zero. + return Ok(vec![]); + } let image_data = self .canvas - .get_image_data(0., 0., width.get().into(), height.get().into()) + .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")?; @@ -373,6 +388,13 @@ impl Canvas { ), } } + + fn clear_rect(&self, x: f64, y: f64, width: f64, height: f64) { + match self { + Self::Canvas { ctx, .. } => ctx.clear_rect(x, y, width, height), + Self::OffscreenCanvas { ctx, .. } => ctx.clear_rect(x, y, width, height), + } + } } #[derive(Debug)] @@ -381,18 +403,12 @@ pub struct BufferImpl<'a, D, W> { } impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - self.imp - .size - .expect("must set size of surface before calling `width()` on the buffer") - .0 + fn width(&self) -> u32 { + self.imp.width } - fn height(&self) -> NonZeroU32 { - self.imp - .size - .expect("must set size of surface before calling `height()` on the buffer") - .1 + fn height(&self) -> u32 { + self.imp.height } fn pixels(&self) -> &[u32] { @@ -413,16 +429,13 @@ impl BufferInterface for BufferImpl<'_, /// Push the buffer to the canvas. fn present(self) -> Result<(), SoftBufferError> { - let (width, height) = self - .imp - .size - .expect("Must set size of surface before calling `present()`"); - self.imp.present_with_damage(&[Rect { + let rect = Rect { x: 0, y: 0, - width, - height, - }]) + width: self.imp.width, + height: self.imp.height, + }; + self.imp.present_with_damage(&[rect]) } fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { diff --git a/src/backends/win32.rs b/src/backends/win32.rs index 75de23e..74110e5 100644 --- a/src/backends/win32.rs +++ b/src/backends/win32.rs @@ -3,15 +3,13 @@ //! This module converts the input buffer into a bitmap and then stretches it to the window. use crate::backend_interface::*; -use crate::{Rect, SoftBufferError}; +use crate::{util, Rect, SoftBufferError}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; use std::io; use std::marker::PhantomData; use std::mem; -use std::num::{NonZeroI32, NonZeroU32}; use std::ptr::{self, NonNull}; -use std::slice; use std::sync::{mpsc, Mutex, OnceLock}; use std::thread; @@ -29,9 +27,7 @@ const ZERO_QUAD: Gdi::RGBQUAD = Gdi::RGBQUAD { struct Buffer { dc: Gdi::HDC, bitmap: Gdi::HBITMAP, - pixels: NonNull, - width: NonZeroI32, - height: NonZeroI32, + pixels: NonNull<[u32]>, presented: bool, } @@ -48,7 +44,7 @@ impl Drop for Buffer { } impl Buffer { - fn new(window_dc: Gdi::HDC, width: NonZeroI32, height: NonZeroI32) -> Self { + fn new(window_dc: Gdi::HDC, width: i32, height: i32) -> Self { let dc = Allocator::get().allocate(window_dc); assert!(!dc.is_null()); @@ -56,8 +52,9 @@ impl Buffer { let bitmap_info = BitmapInfo { bmi_header: Gdi::BITMAPINFOHEADER { biSize: mem::size_of::() as u32, - biWidth: width.get(), - biHeight: -height.get(), + biWidth: width, + // Negative height -> origin is the upper-left corner. + biHeight: -height, biPlanes: 1, biBitCount: 32, biCompression: Gdi::BI_BITFIELDS, @@ -99,6 +96,7 @@ impl Buffer { }; assert!(!bitmap.is_null()); let pixels = NonNull::new(pixels).unwrap(); + let pixels = NonNull::slice_from_raw_parts(pixels, width as usize * height as usize); unsafe { Gdi::SelectObject(dc, bitmap); @@ -107,32 +105,10 @@ impl Buffer { Self { dc, bitmap, - width, - height, pixels, presented: false, } } - - #[inline] - fn pixels(&self) -> &[u32] { - unsafe { - slice::from_raw_parts( - self.pixels.as_ptr(), - i32::from(self.width) as usize * i32::from(self.height) as usize, - ) - } - } - - #[inline] - fn pixels_mut(&mut self) -> &mut [u32] { - unsafe { - slice::from_raw_parts_mut( - self.pixels.as_ptr(), - i32::from(self.width) as usize * i32::from(self.height) as usize, - ) - } - } } /// The handle to a window for software buffering. @@ -145,8 +121,16 @@ pub struct Win32Impl { dc: OnlyUsedFromOrigin, /// The buffer used to hold the image. + /// + /// No buffer -> width and height is zero. buffer: Option, + /// The width of the buffer. + width: u32, + + /// The height of the buffer. + height: u32, + /// The handle for the window. /// /// This should be kept alive in order to keep `window` valid. @@ -174,35 +158,43 @@ struct BitmapInfo { impl Win32Impl { fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { - let buffer = self.buffer.as_mut().unwrap(); - unsafe { + if let Some(buffer) = &mut self.buffer { for rect in damage.iter().copied() { let (x, y, width, height) = (|| { Some(( i32::try_from(rect.x).ok()?, i32::try_from(rect.y).ok()?, - i32::try_from(rect.width.get()).ok()?, - i32::try_from(rect.height.get()).ok()?, + i32::try_from(rect.width).ok()?, + i32::try_from(rect.height).ok()?, )) })() .ok_or(SoftBufferError::DamageOutOfRange { rect })?; - Gdi::BitBlt( - self.dc.0, - x, - y, - width, - height, - buffer.dc, - x, - y, - Gdi::SRCCOPY, - ); + + unsafe { + Gdi::BitBlt( + self.dc.0, + x, + y, + width, + height, + buffer.dc, + x, + y, + Gdi::SRCCOPY, + ) + }; } - // Validate the window. - Gdi::ValidateRect(self.window.0, ptr::null_mut()); + buffer.presented = true; + } else { + // No buffer -> don't draw anything, this is consistent with having a zero-sized buffer. + // + // Once we implement though, + // we'll probably want to clear the window here instead. } - buffer.presented = true; + + // Validate the window. + unsafe { Gdi::ValidateRect(self.window.0, ptr::null_mut()) }; Ok(()) } @@ -240,6 +232,8 @@ impl SurfaceInterface for Win32Im dc: dc.into(), window: hwnd.into(), buffer: None, + width: 0, + height: 0, handle: window, _display: PhantomData, }) @@ -250,30 +244,28 @@ impl SurfaceInterface for Win32Im &self.handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - let (width, height) = (|| { - let width = NonZeroI32::try_from(width).ok()?; - let height = NonZeroI32::try_from(height).ok()?; - Some((width, height)) - })() - .ok_or(SoftBufferError::SizeOutOfRange { width, height })?; - - if let Some(buffer) = self.buffer.as_ref() { - if buffer.width == width && buffer.height == height { - return Ok(()); - } + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { + let (width_i32, height_i32) = util::convert_size::(width, height) + .map_err(|_| SoftBufferError::SizeOutOfRange { width, height })?; + + if self.width == width && self.height == height { + return Ok(()); } - self.buffer = Some(Buffer::new(self.dc.0, width, height)); + // Attempting to create a zero-sized Gdi::HBITMAP returns NULL, so we handle this case + // ourselves. + self.buffer = if width_i32 != 0 && height_i32 != 0 { + Some(Buffer::new(self.dc.0, width_i32, height_i32)) + } else { + None + }; + self.width = width; + self.height = height; Ok(()) } fn buffer_mut(&mut self) -> Result, SoftBufferError> { - if self.buffer.is_none() { - panic!("Must set size of surface before calling `buffer_mut()`"); - } - Ok(BufferImpl(self)) } @@ -287,22 +279,30 @@ impl SurfaceInterface for Win32Im pub struct BufferImpl<'a, D, W>(&'a mut Win32Impl); impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - self.0.buffer.as_ref().unwrap().width.try_into().unwrap() + fn width(&self) -> u32 { + self.0.width } - fn height(&self) -> NonZeroU32 { - self.0.buffer.as_ref().unwrap().height.try_into().unwrap() + fn height(&self) -> u32 { + self.0.height } #[inline] fn pixels(&self) -> &[u32] { - self.0.buffer.as_ref().unwrap().pixels() + if let Some(buffer) = &self.0.buffer { + unsafe { buffer.pixels.as_ref() } + } else { + &[] + } } #[inline] fn pixels_mut(&mut self) -> &mut [u32] { - self.0.buffer.as_mut().unwrap().pixels_mut() + if let Some(buffer) = &mut self.0.buffer { + unsafe { buffer.pixels.as_mut() } + } else { + &mut [] + } } fn age(&self) -> u8 { @@ -313,20 +313,16 @@ impl BufferInterface for BufferImpl<'_, } fn present(self) -> Result<(), SoftBufferError> { - let imp = self.0; - let buffer = imp.buffer.as_ref().unwrap(); - imp.present_with_damage(&[Rect { + self.0.present_with_damage(&[Rect { x: 0, y: 0, - // We know width/height will be non-negative - width: buffer.width.try_into().unwrap(), - height: buffer.height.try_into().unwrap(), + width: self.0.width, + height: self.0.height, }]) } fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { - let imp = self.0; - imp.present_with_damage(damage) + self.0.present_with_damage(damage) } } diff --git a/src/backends/x11.rs b/src/backends/x11.rs index 711b0b7..ae2dad6 100644 --- a/src/backends/x11.rs +++ b/src/backends/x11.rs @@ -22,7 +22,7 @@ use std::{ fmt, fs::File, io, mem, - num::{NonZeroU16, NonZeroU32}, + num::NonZeroU32, ptr::{null_mut, NonNull}, slice, sync::Arc, @@ -149,8 +149,11 @@ pub struct X11Impl { /// Buffer has been presented. buffer_presented: bool, - /// The current buffer width/height. - size: Option<(NonZeroU16, NonZeroU16)>, + /// The current buffer width. + width: u16, + + /// The current buffer height. + height: u16, /// Keep the window alive. window_handle: W, @@ -300,7 +303,8 @@ impl SurfaceInterface fo visual_id, buffer, buffer_presented: false, - size: None, + width: 0, + height: 0, window_handle: window_src, }) } @@ -310,7 +314,7 @@ impl SurfaceInterface fo &self.window_handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { tracing::trace!( "resize: window={:X}, size={}x{}", self.window, @@ -319,22 +323,18 @@ impl SurfaceInterface fo ); // Width and height should fit in u16. - let width: NonZeroU16 = width - .try_into() - .or(Err(SoftBufferError::SizeOutOfRange { width, height }))?; - let height: NonZeroU16 = height.try_into().or(Err(SoftBufferError::SizeOutOfRange { - width: width.into(), - height, - }))?; - - if self.size != Some((width, height)) { + let (width, height) = util::convert_size::(width, height) + .map_err(|_| SoftBufferError::SizeOutOfRange { width, height })?; + + if self.width != width && self.height != height { self.buffer_presented = false; self.buffer - .resize(self.display.connection(), width.get(), height.get()) + .resize(self.display.connection(), width, height) .swbuf_err("Failed to resize X11 buffer")?; // We successfully resized the buffer. - self.size = Some((width, height)); + self.width = width; + self.height = height; } Ok(()) @@ -353,10 +353,6 @@ impl SurfaceInterface fo fn fetch(&mut self) -> Result, SoftBufferError> { tracing::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 @@ -366,8 +362,8 @@ impl SurfaceInterface fo self.window, 0, 0, - width.get(), - height.get(), + self.width, + self.height, u32::MAX, ) .swbuf_err("Failed to send image fetching request")? @@ -393,12 +389,12 @@ pub struct BufferImpl<'a, D: ?Sized, W: ?Sized>(&'a mut X11Impl); impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - self.0.size.unwrap().0.into() + fn width(&self) -> u32 { + self.0.width as u32 } - fn height(&self) -> NonZeroU32 { - self.0.size.unwrap().1.into() + fn height(&self) -> u32 { + self.0.height as u32 } #[inline] @@ -425,10 +421,6 @@ impl BufferInterface fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { let imp = self.0; - let (surface_width, surface_height) = imp - .size - .expect("Must set size of surface before calling `present_with_damage()`"); - tracing::trace!("present: window={:X}", imp.window); match imp.buffer { @@ -442,8 +434,8 @@ impl BufferInterface xproto::ImageFormat::Z_PIXMAP, imp.window, imp.gc, - surface_width.get(), - surface_height.get(), + imp.width, + imp.height, 0, 0, 0, @@ -469,8 +461,8 @@ impl BufferInterface u16::try_from(rect.y).ok()?, i16::try_from(rect.x).ok()?, i16::try_from(rect.y).ok()?, - u16::try_from(rect.width.get()).ok()?, - u16::try_from(rect.height.get()).ok()?, + u16::try_from(rect.width).ok()?, + u16::try_from(rect.height).ok()?, )) })( ) @@ -480,8 +472,8 @@ impl BufferInterface .shm_put_image( imp.window, imp.gc, - surface_width.get(), - surface_height.get(), + imp.width, + imp.height, src_x, src_y, width, @@ -513,16 +505,13 @@ impl BufferInterface } fn present(self) -> Result<(), SoftBufferError> { - let (width, height) = self - .0 - .size - .expect("Must set size of surface before calling `present()`"); - self.present_with_damage(&[Rect { + let rect = Rect { x: 0, y: 0, - width: width.into(), - height: height.into(), - }]) + width: self.0.width.into(), + height: self.0.height.into(), + }; + self.present_with_damage(&[rect]) } } diff --git a/src/error.rs b/src/error.rs index b79be6b..4405fe7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,6 @@ use raw_window_handle::{HandleError, RawDisplayHandle, RawWindowHandle}; use std::error::Error; use std::fmt; -use std::num::NonZeroU32; #[derive(Debug)] #[non_exhaustive] @@ -80,10 +79,10 @@ pub enum SoftBufferError { /// The provided size is outside of the range supported by the backend. SizeOutOfRange { /// The width that was out of range. - width: NonZeroU32, + width: u32, /// The height that was out of range. - height: NonZeroU32, + height: u32, }, /// The provided damage rect is outside of the range supported by the backend. diff --git a/src/lib.rs b/src/lib.rs index 7923c3e..72fbe89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ mod util; use std::cell::Cell; use std::marker::PhantomData; -use std::num::NonZeroU32; use std::ops; use std::sync::Arc; @@ -67,9 +66,9 @@ pub struct Rect { /// y coordinate of top left corner pub y: u32, /// width - pub width: NonZeroU32, + pub width: u32, /// height - pub height: NonZeroU32, + pub height: u32, } /// A surface for drawing to a window with software buffers. @@ -111,7 +110,7 @@ impl Surface { /// in the upper-left corner of the window. It is recommended in most production use cases /// to have the buffer fill the entire window. Use your windowing library to find the size /// of the window. - pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { + pub fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { self.surface_impl.resize(width, height) } @@ -127,9 +126,13 @@ impl Surface { 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. + /// Return a [`Buffer`] that the next frame should be rendered into. + /// + /// The buffer initially empty, you'll want to set an appropriate size for it with + /// [`Surface::resize`]. + /// + /// The initial contents of the buffer may be zeroed, or may contain a previous frame. Call + /// [`Buffer::age`] to determine this. /// /// ## Platform Dependent Behavior /// @@ -209,10 +212,10 @@ pub struct Buffer<'a, D, W> { impl Buffer<'_, D, W> { /// The amount of pixels wide the buffer is. - pub fn width(&self) -> NonZeroU32 { + pub fn width(&self) -> u32 { let width = self.buffer_impl.width(); debug_assert_eq!( - width.get() as usize * self.buffer_impl.height().get() as usize, + width as usize * self.buffer_impl.height() as usize, self.len(), "buffer must be sized correctly" ); @@ -220,10 +223,10 @@ impl Buffer<'_, D, W> { } /// The amount of pixels tall the buffer is. - pub fn height(&self) -> NonZeroU32 { + pub fn height(&self) -> u32 { let height = self.buffer_impl.height(); debug_assert_eq!( - height.get() as usize * self.buffer_impl.width().get() as usize, + height as usize * self.buffer_impl.width() as usize, self.len(), "buffer must be sized correctly" ); diff --git a/src/util.rs b/src/util.rs index 572de5e..c90ba8c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,7 +3,6 @@ use std::cmp; use std::fmt; -use std::num::NonZeroU32; use std::ops; use crate::Rect; @@ -74,8 +73,8 @@ pub(crate) fn union_damage(damage: &[Rect]) -> Option { .map(|rect| Region { left: rect.x, top: rect.y, - right: rect.x + rect.width.get(), - bottom: rect.y + rect.height.get(), + right: rect.x + rect.width, + bottom: rect.y + rect.height, }) .reduce(|mut prev, next| { prev.left = cmp::min(prev.left, next.left); @@ -88,9 +87,13 @@ pub(crate) fn union_damage(damage: &[Rect]) -> Option { Some(Rect { x: region.left, y: region.top, - width: NonZeroU32::new(region.right - region.left) + width: region + .right + .checked_sub(region.left) .expect("`right` must always be bigger then `left`"), - height: NonZeroU32::new(region.bottom - region.top) + height: region + .bottom + .checked_sub(region.top) .expect("`bottom` must always be bigger then `top`"), }) } @@ -118,6 +121,12 @@ impl ops::DerefMut for PixelBuffer { } } +pub(crate) fn convert_size>(width: u32, height: u32) -> Result<(T, T), T::Error> { + let width = T::try_from(width)?; + let height = T::try_from(height)?; + Ok((width, height)) +} + #[cfg(test)] mod tests { use super::*;