diff --git a/src/error.rs b/src/error.rs index 1fd8ba2..8e538f2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -36,6 +36,11 @@ pub enum SoftBufferError { height: NonZeroU32, }, + #[error( + "Damage rect {}x{} at ({}, {}) out of range for backend.", .rect.width, .rect.height, .rect.x, .rect.y, + )] + DamageOutOfRange { rect: crate::Rect }, + #[error("Platform error")] PlatformError(Option, Option>), } diff --git a/src/lib.rs b/src/lib.rs index ef82627..52d5ead 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,13 +233,13 @@ impl Context { #[derive(Clone, Copy, Debug)] pub struct Rect { /// x coordinate of top left corner - pub x: i32, + pub x: u32, /// y coordinate of top left corner - pub y: i32, + pub y: u32, /// width - pub width: i32, + pub width: NonZeroU32, /// height - pub height: i32, + pub height: NonZeroU32, } /// A surface for drawing to a window with software buffers. diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 596907c..ce20b1e 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -159,15 +159,18 @@ impl WaylandImpl { if self.surface.version() < 4 { self.surface.damage(0, 0, i32::MAX, i32::MAX); } else { - for Rect { - x, - y, - width, - height, - } in damage - { + for rect in damage { // Introduced in version 4, it is an error to use this request in version 3 or lower. - self.surface.damage_buffer(*x, *y, *width, *height); + 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()?, + )) + })() + .ok_or(SoftBufferError::DamageOutOfRange { rect: *rect })?; + self.surface.damage_buffer(x, y, width, height); } } @@ -212,8 +215,9 @@ impl<'a> BufferImpl<'a> { imp.present_with_damage(&[Rect { x: 0, y: 0, - width: width.get(), - height: height.get(), + // We know width/height will be non-negative + width: width.try_into().unwrap(), + height: height.try_into().unwrap(), }]) } } diff --git a/src/web.rs b/src/web.rs index 85e1ae4..bdb7781 100644 --- a/src/web.rs +++ b/src/web.rs @@ -45,11 +45,8 @@ pub struct WebImpl { /// Buffer has been presented. buffer_presented: bool, - /// The current width of the canvas. - width: u32, - - /// The current height of the canvas. - height: u32, + /// The current canvas width/height. + size: Option<(NonZeroU32, NonZeroU32)>, } impl WebImpl { @@ -82,8 +79,7 @@ impl WebImpl { ctx, buffer: Vec::new(), buffer_presented: false, - width: 0, - height: 0, + size: None, }) } @@ -93,16 +89,12 @@ impl WebImpl { width: NonZeroU32, height: NonZeroU32, ) -> Result<(), SoftBufferError> { - let width = width.get(); - let height = height.get(); - - if width != self.width || height != self.height { + if self.size != Some((width, height)) { self.buffer_presented = false; - self.buffer.resize(total_len(width, height), 0); - self.canvas.set_width(width); - self.canvas.set_height(height); - self.width = width; - self.height = height; + 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)); } Ok(()) @@ -114,6 +106,9 @@ impl WebImpl { } fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { + let (width, _height) = self + .size + .expect("Must set size of surface before calling `present_with_damage()`"); // Create a bitmap from the buffer. let bitmap: Vec<_> = self .buffer @@ -140,33 +135,27 @@ impl WebImpl { let array = Uint8Array::new_with_length(bitmap.len() as u32); array.copy_from(&bitmap); let array = Uint8ClampedArray::new(&array); - ImageDataExt::new(array, self.width) + ImageDataExt::new(array, width.get()) .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), self.width); + ImageData::new_with_u8_clamped_array(wasm_bindgen::Clamped(&bitmap), width.get()); // This should only throw an error if the buffer we pass's size is incorrect. let image_data = result.unwrap(); - for Rect { - x, - y, - width, - height, - } in damage - { + for rect in damage { // This can only throw an error if `data` is detached, which is impossible. self.ctx .put_image_data_with_dirty_x_and_dirty_y_and_dirty_width_and_dirty_height( &image_data, - (*x).into(), - (*y).into(), - (*x).into(), - (*y).into(), - (*width).into(), - (*height).into(), + rect.x.into(), + rect.y.into(), + rect.x.into(), + rect.y.into(), + rect.width.get().into(), + rect.height.get().into(), ) .unwrap(); } @@ -217,16 +206,10 @@ impl<'a> BufferImpl<'a> { /// Push the buffer to the canvas. pub fn present(self) -> Result<(), SoftBufferError> { - let (width, height) = (|| { - let width = i32::try_from(self.imp.width).ok()?; - let height = i32::try_from(self.imp.height).ok()?; - Some((width, height)) - })() - .ok_or(SoftBufferError::SizeOutOfRange { - width: NonZeroU32::new(self.imp.width).unwrap(), - height: NonZeroU32::new(self.imp.height).unwrap(), - })?; - + let (width, height) = self + .imp + .size + .expect("Must set size of surface before calling `present()`"); self.imp.present_with_damage(&[Rect { x: 0, y: 0, diff --git a/src/win32.rs b/src/win32.rs index ba6a829..6607c98 100644 --- a/src/win32.rs +++ b/src/win32.rs @@ -208,24 +208,17 @@ impl Win32Impl { fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { let buffer = self.buffer.as_mut().unwrap(); unsafe { - for Rect { - x, - y, - width, - height, - } in damage - { - Gdi::BitBlt( - self.dc, - *x, - *y, - *width, - *height, - buffer.dc, - *x, - *y, - Gdi::SRCCOPY, - ); + 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()?, + )) + })() + .ok_or(SoftBufferError::DamageOutOfRange { rect })?; + Gdi::BitBlt(self.dc, x, y, width, height, buffer.dc, x, y, Gdi::SRCCOPY); } // Validate the window. @@ -263,8 +256,9 @@ impl<'a> BufferImpl<'a> { imp.present_with_damage(&[Rect { x: 0, y: 0, - width: buffer.width.get(), - height: buffer.height.get(), + // We know width/height will be non-negative + width: buffer.width.try_into().unwrap(), + height: buffer.height.try_into().unwrap(), }]) } diff --git a/src/x11.rs b/src/x11.rs index 4c1abc7..3a9b518 100644 --- a/src/x11.rs +++ b/src/x11.rs @@ -10,7 +10,12 @@ use crate::{Rect, SoftBufferError}; use nix::libc::{shmat, shmctl, shmdt, shmget, IPC_PRIVATE, IPC_RMID}; use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle, XlibDisplayHandle, XlibWindowHandle}; use std::ptr::{null_mut, NonNull}; -use std::{fmt, io, mem, num::NonZeroU32, rc::Rc, slice}; +use std::{ + fmt, io, mem, + num::{NonZeroU16, NonZeroU32}, + rc::Rc, + slice, +}; use x11_dl::xlib::Display; use x11_dl::xlib_xcb::Xlib_xcb; @@ -110,11 +115,8 @@ pub struct X11Impl { /// Buffer has been presented. buffer_presented: bool, - /// The current buffer width. - width: u16, - - /// The current buffer height. - height: u16, + /// The current buffer width/height. + size: Option<(NonZeroU16, NonZeroU16)>, } /// The buffer that is being drawn to. @@ -227,8 +229,7 @@ impl X11Impl { depth: geometry_reply.depth, buffer, buffer_presented: false, - width: 0, - height: 0, + size: None, }) } @@ -246,27 +247,22 @@ impl X11Impl { ); // Width and height should fit in u16. - let width: u16 = width - .get() + let width: NonZeroU16 = width .try_into() .or(Err(SoftBufferError::SizeOutOfRange { width, height }))?; - let height: u16 = height - .get() - .try_into() - .or(Err(SoftBufferError::SizeOutOfRange { - width: NonZeroU32::new(width.into()).unwrap(), - height, - }))?; + let height: NonZeroU16 = height.try_into().or(Err(SoftBufferError::SizeOutOfRange { + width: width.into(), + height, + }))?; - if width != self.width || height != self.height { + if self.size != Some((width, height)) { self.buffer_presented = false; self.buffer - .resize(&self.display.connection, width, height) + .resize(&self.display.connection, width.get(), height.get()) .swbuf_err("Failed to resize X11 buffer")?; // We successfully resized the buffer. - self.width = width; - self.height = height; + self.size = Some((width, height)); } Ok(()) @@ -311,6 +307,10 @@ impl<'a> BufferImpl<'a> { pub 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()`"); + log::trace!("present: window={:X}", imp.window); match imp.buffer { @@ -324,8 +324,8 @@ impl<'a> BufferImpl<'a> { xproto::ImageFormat::Z_PIXMAP, imp.window, imp.gc, - imp.width, - imp.height, + surface_width.get(), + surface_height.get(), 0, 0, 0, @@ -334,6 +334,7 @@ impl<'a> BufferImpl<'a> { ) .map(|c| c.ignore_error()) .push_err() + .swbuf_err("Failed to draw image to window")?; } Buffer::Shm(ref mut shm) => { @@ -343,46 +344,50 @@ impl<'a> BufferImpl<'a> { if let Some((_, segment_id)) = shm.seg { damage .iter() - .try_for_each( - |Rect { - x, - y, - width, - height, - }| { - imp.display - .connection - .shm_put_image( - imp.window, - imp.gc, - imp.width, - imp.height, - *x as u16, - *y as u16, - *width as u16, - *height as u16, - *x as i16, - *y as i16, - imp.depth, - xproto::ImageFormat::Z_PIXMAP.into(), - false, - segment_id, - 0, - ) - .push_err() - .map(|c| c.ignore_error()) - }, - ) + .try_for_each(|rect| { + let (src_x, src_y, dst_x, dst_y, width, height) = (|| { + Some(( + u16::try_from(rect.x).ok()?, + 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()?, + )) + })( + ) + .ok_or(SoftBufferError::DamageOutOfRange { rect: *rect })?; + imp.display + .connection + .shm_put_image( + imp.window, + imp.gc, + surface_width.get(), + surface_height.get(), + src_x, + src_y, + width, + height, + dst_x, + dst_y, + imp.depth, + xproto::ImageFormat::Z_PIXMAP.into(), + false, + segment_id, + 0, + ) + .push_err() + .map(|c| c.ignore_error()) + .swbuf_err("Failed to draw image to window") + }) .and_then(|()| { // Send a short request to act as a notification for when the X server is done processing the image. shm.begin_wait(&imp.display.connection) - }) - } else { - Ok(()) + .swbuf_err("Failed to draw image to window") + })?; } } } - .swbuf_err("Failed to draw image to window")?; imp.buffer_presented = true; @@ -390,13 +395,15 @@ impl<'a> BufferImpl<'a> { } pub fn present(self) -> Result<(), SoftBufferError> { - let width = self.0.width.into(); - let height = self.0.height.into(); + let (width, height) = self + .0 + .size + .expect("Must set size of surface before calling `present()`"); self.present_with_damage(&[Rect { x: 0, y: 0, - width, - height, + width: width.into(), + height: height.into(), }]) } }