diff --git a/src/cg.rs b/src/cg.rs index c25eaedc..647e51af 100644 --- a/src/cg.rs +++ b/src/cg.rs @@ -1,4 +1,4 @@ -use crate::SoftBufferError; +use crate::{Rect, SoftBufferError}; use core_graphics::base::{ kCGBitmapByteOrder32Little, kCGImageAlphaNoneSkipFirst, kCGRenderingIntentDefault, }; @@ -92,6 +92,10 @@ impl<'a> BufferImpl<'a> { &mut self.buffer } + pub fn age(&self) -> u8 { + 0 + } + pub fn present(self) -> Result<(), SoftBufferError> { let data_provider = CGDataProvider::from_buffer(Arc::new(Buffer(self.buffer))); let image = CGImage::new( @@ -124,6 +128,10 @@ impl<'a> BufferImpl<'a> { Ok(()) } + + pub fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> { + self.present() + } } impl Drop for CGImpl { diff --git a/src/error.rs b/src/error.rs index 26e1aa7c..700f87b5 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 e1365c52..0c39dd1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,6 +137,15 @@ macro_rules! make_dispatch { } } + pub fn age(&self) -> u8 { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.age(), + )* + } + } + pub fn present(self) -> Result<(), SoftBufferError> { match self { $( @@ -145,6 +154,15 @@ macro_rules! make_dispatch { )* } } + + pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.present_with_damage(damage), + )* + } + } } }; } @@ -220,6 +238,19 @@ impl Context { } } +/// A rectangular region of the buffer coordinate space. +#[derive(Clone, Copy, Debug)] +pub struct Rect { + /// x coordinate of top left corner + pub x: u32, + /// y coordinate of top left corner + pub y: u32, + /// width + pub width: NonZeroU32, + /// height + pub height: NonZeroU32, +} + /// A surface for drawing to a window with software buffers. pub struct Surface { /// This is boxed so that `Surface` is the same size on every platform. @@ -329,7 +360,7 @@ impl Surface { /// 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. + /// may contain a previous frame. Call [`Buffer::age`] to determine this. pub fn buffer_mut(&mut self) -> Result { Ok(Buffer { buffer_impl: self.surface_impl.buffer_mut()?, @@ -380,6 +411,16 @@ pub struct Buffer<'a> { } impl<'a> Buffer<'a> { + /// Is age is the number of frames ago this buffer was last presented. So if the value is + /// `1`, it is the same as the last frame, and if it is `2`, it is the same as the frame + /// before that (for backends using double buffering). If the value is `0`, it is a new + /// buffer that has unspecified contents. + /// + /// This can be used to update only a portion of the buffer. + pub fn age(&self) -> u8 { + self.buffer_impl.age() + } + /// Presents buffer to the window. /// /// # Platform dependent behavior @@ -395,6 +436,20 @@ impl<'a> Buffer<'a> { pub fn present(self) -> Result<(), SoftBufferError> { self.buffer_impl.present() } + + /// Presents buffer to the window, with damage regions. + /// + /// # Platform dependent behavior + /// + /// Supported on: + /// - Wayland + /// - X, when XShm is available + /// - Win32 + /// + /// Otherwise this is equivalent to [`Self::present`]. + pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { + self.buffer_impl.present_with_damage(damage) + } } impl<'a> ops::Deref for Buffer<'a> { diff --git a/src/orbital.rs b/src/orbital.rs index d69becad..407f3b7c 100644 --- a/src/orbital.rs +++ b/src/orbital.rs @@ -1,7 +1,7 @@ use raw_window_handle::OrbitalWindowHandle; use std::{cmp, num::NonZeroU32, slice, str}; -use crate::SoftBufferError; +use crate::{Rect, SoftBufferError}; struct OrbitalMap { address: usize, @@ -57,6 +57,7 @@ pub struct OrbitalImpl { handle: OrbitalWindowHandle, width: u32, height: u32, + presented: bool, } impl OrbitalImpl { @@ -65,12 +66,18 @@ impl OrbitalImpl { handle, width: 0, height: 0, + presented: false, }) } pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - self.width = width.get(); - self.height = height.get(); + let width = width.get(); + let height = height.get(); + if width != self.width && height != self.height { + self.presented = false; + self.width = width; + self.height = height; + } Ok(()) } @@ -177,11 +184,19 @@ impl<'a> BufferImpl<'a> { } } + pub fn age(&self) -> u8 { + match self.pixels { + Pixels::Mapping(_) if self.imp.presented => 1, + _ => 0, + } + } + pub fn present(self) -> Result<(), SoftBufferError> { match self.pixels { Pixels::Mapping(mapping) => { drop(mapping); syscall::fsync(self.imp.window_fd()).expect("failed to sync orbital window"); + self.imp.presented = true; } Pixels::Buffer(buffer) => { self.imp @@ -191,4 +206,8 @@ impl<'a> BufferImpl<'a> { Ok(()) } + + pub fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> { + self.present() + } } diff --git a/src/wayland/buffer.rs b/src/wayland/buffer.rs index e17961a3..ed8855fc 100644 --- a/src/wayland/buffer.rs +++ b/src/wayland/buffer.rs @@ -90,6 +90,7 @@ pub(super) struct WaylandBuffer { width: i32, height: i32, released: Arc, + pub age: u8, } impl WaylandBuffer { @@ -125,6 +126,7 @@ impl WaylandBuffer { width, height, released, + age: 0, } } diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index a2606a77..05b409f8 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -1,4 +1,4 @@ -use crate::{error::SwResultExt, util, SoftBufferError}; +use crate::{error::SwResultExt, util, Rect, SoftBufferError}; use raw_window_handle::{WaylandDisplayHandle, WaylandWindowHandle}; use std::{ cell::RefCell, @@ -125,72 +125,107 @@ impl WaylandImpl { )); }; - Ok(BufferImpl(util::BorrowStack::new(self, |buffer| { - Ok(unsafe { buffer.buffers.as_mut().unwrap().1.mapped_mut() }) - })?)) + 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() }) + })?, + age, + }) } /// Fetch the buffer from the window. pub fn fetch(&mut self) -> Result, SoftBufferError> { Err(SoftBufferError::Unimplemented) } -} - -pub struct BufferImpl<'a>(util::BorrowStack<'a, WaylandImpl, [u32]>); - -impl<'a> BufferImpl<'a> { - #[inline] - pub fn pixels(&self) -> &[u32] { - self.0.member() - } - - #[inline] - pub fn pixels_mut(&mut self) -> &mut [u32] { - self.0.member_mut() - } - - pub fn present(self) -> Result<(), SoftBufferError> { - let imp = self.0.into_container(); - let (width, height) = imp - .size - .expect("Must set size of surface before calling `present()`"); - - let _ = imp + fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { + let _ = self .display .event_queue .borrow_mut() .dispatch_pending(&mut State); - if let Some((front, back)) = &mut imp.buffers { + if let Some((front, back)) = &mut self.buffers { + front.age = 1; + if back.age != 0 { + back.age += 1; + } + // Swap front and back buffer std::mem::swap(front, back); - front.attach(&imp.surface); - - // FIXME: Proper damaging mechanism. - // - // In order to propagate changes on compositors which track damage, for now damage the entire surface. - if imp.surface.version() < 4 { - // FIXME: Accommodate scale factor since wl_surface::damage is in terms of surface coordinates while - // wl_surface::damage_buffer is in buffer coordinates. - // - // i32::MAX is a valid damage box (most compositors interpret the damage box as "the entire surface") - imp.surface.damage(0, 0, i32::MAX, i32::MAX); + front.attach(&self.surface); + + // Like Mesa's EGL/WSI implementation, we damage the whole buffer with `i32::MAX` if + // the compositor doesn't support `damage_buffer`. + // https://bugs.freedesktop.org/show_bug.cgi?id=78190 + if self.surface.version() < 4 { + self.surface.damage(0, 0, i32::MAX, i32::MAX); } else { - // Introduced in version 4, it is an error to use this request in version 3 or lower. - imp.surface.damage_buffer(0, 0, width.get(), height.get()); + for rect in damage { + // Introduced in version 4, it is an error to use this request in version 3 or lower. + 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); + } } - imp.surface.commit(); + self.surface.commit(); } - let _ = imp.display.event_queue.borrow_mut().flush(); + let _ = self.display.event_queue.borrow_mut().flush(); Ok(()) } } +pub struct BufferImpl<'a> { + stack: util::BorrowStack<'a, WaylandImpl, [u32]>, + age: u8, +} + +impl<'a> BufferImpl<'a> { + #[inline] + pub fn pixels(&self) -> &[u32] { + self.stack.member() + } + + #[inline] + pub fn pixels_mut(&mut self) -> &mut [u32] { + self.stack.member_mut() + } + + pub fn age(&self) -> u8 { + self.age + } + + pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { + self.stack.into_container().present_with_damage(damage) + } + + pub 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()`"); + 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(), + }]) + } +} + impl Dispatch for State { fn event( _: &mut State, diff --git a/src/web.rs b/src/web.rs index 95bcfa72..96b9f038 100644 --- a/src/web.rs +++ b/src/web.rs @@ -9,7 +9,7 @@ use web_sys::HtmlCanvasElement; use web_sys::ImageData; use crate::error::SwResultExt; -use crate::SoftBufferError; +use crate::{Rect, SoftBufferError}; use std::convert::TryInto; use std::marker::PhantomData; use std::num::NonZeroU32; @@ -42,11 +42,11 @@ pub struct WebImpl { /// The buffer that we're drawing to. buffer: Vec, - /// The current width of the canvas. - width: u32, + /// Buffer has been presented. + buffer_presented: bool, - /// The current height of the canvas. - height: u32, + /// The current canvas width/height. + size: Option<(NonZeroU32, NonZeroU32)>, } impl WebImpl { @@ -78,8 +78,8 @@ impl WebImpl { canvas, ctx, buffer: Vec::new(), - width: 0, - height: 0, + buffer_presented: false, + size: None, }) } @@ -89,14 +89,14 @@ impl WebImpl { width: NonZeroU32, height: NonZeroU32, ) -> Result<(), SoftBufferError> { - let width = width.get(); - let height = height.get(); - - self.buffer.resize(total_len(width, height), 0); - self.canvas.set_width(width); - self.canvas.set_height(height); - self.width = width; - self.height = height; + if self.size != Some((width, 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)); + } + Ok(()) } @@ -105,11 +105,75 @@ impl WebImpl { Ok(BufferImpl { imp: self }) } + 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 + .iter() + .copied() + .flat_map(|pixel| [(pixel >> 16) as u8, (pixel >> 8) as u8, pixel as u8, 255]) + .collect(); + + #[cfg(target_feature = "atomics")] + let result = { + use js_sys::{Uint8Array, Uint8ClampedArray}; + use wasm_bindgen::prelude::wasm_bindgen; + use wasm_bindgen::JsValue; + + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(js_name = ImageData)] + type ImageDataExt; + + #[wasm_bindgen(catch, constructor, js_class = ImageData)] + fn new(array: Uint8ClampedArray, sw: u32) -> Result; + } + + let array = Uint8Array::new_with_length(bitmap.len() as u32); + array.copy_from(&bitmap); + let array = Uint8ClampedArray::new(&array); + 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), width.get()); + // This should only throw an error if the buffer we pass's size is incorrect. + let image_data = result.unwrap(); + + 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, + rect.x.into(), + rect.y.into(), + rect.x.into(), + rect.y.into(), + rect.width.get().into(), + rect.height.get().into(), + ) + .unwrap(); + } + + self.buffer_presented = true; + + Ok(()) + } + /// Fetch the buffer from the window. pub fn fetch(&mut self) -> Result, 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., self.width.into(), self.height.into()) + .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")?; @@ -157,49 +221,30 @@ impl<'a> BufferImpl<'a> { &mut self.imp.buffer } + pub fn age(&self) -> u8 { + if self.imp.buffer_presented { + 1 + } else { + 0 + } + } + /// Push the buffer to the canvas. pub fn present(self) -> Result<(), SoftBufferError> { - // Create a bitmap from the buffer. - let bitmap: Vec<_> = self + let (width, height) = self .imp - .buffer - .iter() - .copied() - .flat_map(|pixel| [(pixel >> 16) as u8, (pixel >> 8) as u8, pixel as u8, 255]) - .collect(); - - #[cfg(target_feature = "atomics")] - let result = { - use js_sys::{Uint8Array, Uint8ClampedArray}; - use wasm_bindgen::prelude::wasm_bindgen; - use wasm_bindgen::JsValue; - - #[wasm_bindgen] - extern "C" { - #[wasm_bindgen(js_name = ImageData)] - type ImageDataExt; - - #[wasm_bindgen(catch, constructor, js_class = ImageData)] - fn new(array: Uint8ClampedArray, sw: u32) -> Result; - } - - let array = Uint8Array::new_with_length(bitmap.len() as u32); - array.copy_from(&bitmap); - let array = Uint8ClampedArray::new(&array); - ImageDataExt::new(array, self.imp.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), self.imp.width); - // This should only throw an error if the buffer we pass's size is incorrect. - 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.).unwrap(); + .size + .expect("Must set size of surface before calling `present()`"); + self.imp.present_with_damage(&[Rect { + x: 0, + y: 0, + width, + height, + }]) + } - Ok(()) + pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { + self.imp.present_with_damage(damage) } } diff --git a/src/win32.rs b/src/win32.rs index 5858070d..3b99bf78 100644 --- a/src/win32.rs +++ b/src/win32.rs @@ -2,7 +2,7 @@ //! //! This module converts the input buffer into a bitmap and then stretches it to the window. -use crate::SoftBufferError; +use crate::{Rect, SoftBufferError}; use raw_window_handle::Win32WindowHandle; use std::io; @@ -27,6 +27,7 @@ struct Buffer { pixels: NonNull, width: NonZeroI32, height: NonZeroI32, + presented: bool, } impl Drop for Buffer { @@ -101,6 +102,7 @@ impl Buffer { width, height, pixels, + presented: false, } } @@ -203,6 +205,30 @@ impl Win32Impl { Ok(BufferImpl(self)) } + fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { + let buffer = self.buffer.as_mut().unwrap(); + unsafe { + 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. + Gdi::ValidateRect(self.window, ptr::null_mut()); + } + buffer.presented = true; + + Ok(()) + } + /// Fetch the buffer from the window. pub fn fetch(&mut self) -> Result, SoftBufferError> { let buffer = self.buffer.as_ref().unwrap(); @@ -245,26 +271,27 @@ impl<'a> BufferImpl<'a> { self.0.buffer.as_mut().unwrap().pixels_mut() } + pub fn age(&self) -> u8 { + match self.0.buffer.as_ref() { + Some(buffer) if buffer.presented => 1, + _ => 0, + } + } + pub fn present(self) -> Result<(), SoftBufferError> { let imp = self.0; let buffer = imp.buffer.as_ref().unwrap(); - unsafe { - Gdi::BitBlt( - imp.dc, - 0, - 0, - buffer.width.get(), - buffer.height.get(), - buffer.dc, - 0, - 0, - Gdi::SRCCOPY, - ); - - // Validate the window. - Gdi::ValidateRect(imp.window, ptr::null_mut()); - } + imp.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(), + }]) + } - Ok(()) + pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { + let imp = self.0; + imp.present_with_damage(damage) } } diff --git a/src/x11.rs b/src/x11.rs index 5c6544ec..f9edd448 100644 --- a/src/x11.rs +++ b/src/x11.rs @@ -6,11 +6,16 @@ #![allow(clippy::uninlined_format_args)] use crate::error::SwResultExt; -use crate::SoftBufferError; +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,11 @@ pub struct X11Impl { /// The buffer we draw to. buffer: Buffer, - /// The current buffer width. - width: u16, + /// Buffer has been presented. + buffer_presented: bool, - /// The current buffer height. - height: u16, + /// The current buffer width/height. + size: Option<(NonZeroU16, NonZeroU16)>, } /// The buffer that is being drawn to. @@ -256,8 +261,8 @@ impl X11Impl { depth: geometry_reply.depth, visual_id, buffer, - width: 0, - height: 0, + buffer_presented: false, + size: None, }) } @@ -275,26 +280,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(()) @@ -315,6 +316,10 @@ impl X11Impl { pub fn fetch(&mut self) -> Result, 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 @@ -324,8 +329,8 @@ impl X11Impl { self.window, 0, 0, - self.width, - self.height, + width.get(), + height.get(), u32::MAX, ) .swbuf_err("Failed to send image fetching request")? @@ -360,13 +365,25 @@ impl<'a> BufferImpl<'a> { unsafe { self.0.buffer.buffer_mut() } } + pub fn age(&self) -> u8 { + if self.0.buffer_presented { + 1 + } else { + 0 + } + } + /// Push the buffer to the window. - pub fn present(self) -> Result<(), SoftBufferError> { + 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); - let result = match imp.buffer { + match imp.buffer { Buffer::Wire(ref wire) => { // This is a suboptimal strategy, raise a stink in the debug logs. log::debug!("Falling back to non-SHM method for window drawing."); @@ -377,8 +394,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, @@ -387,6 +404,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) => { @@ -394,38 +412,69 @@ impl<'a> BufferImpl<'a> { // SAFETY: We know that we called finish_wait() before this. // Put the image into the window. if let Some((_, segment_id)) = shm.seg { - imp.display - .connection - .shm_put_image( - imp.window, - imp.gc, - imp.width, - imp.height, - 0, - 0, - imp.width, - imp.height, - 0, - 0, - imp.depth, - xproto::ImageFormat::Z_PIXMAP.into(), - false, - segment_id, - 0, - ) - .push_err() - .map(|c| c.ignore_error()) + damage + .iter() + .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") + })?; } } - }; + } + + imp.buffer_presented = true; - result.swbuf_err("Failed to draw image to window") + Ok(()) + } + + pub 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 { + x: 0, + y: 0, + width: width.into(), + height: height.into(), + }]) } }