diff --git a/CHANGELOG.md b/CHANGELOG.md index e806e03..076936e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Bump MSRV to Rust 1.71. - Make `Context` cloneable. - Added `Buffer::width()` and `Buffer::height()` getters. +- `Context`, `Surface` and `Buffer` now implement `Debug`. # 0.4.6 diff --git a/src/backend_dispatch.rs b/src/backend_dispatch.rs index 5a34a7c..0c36a8c 100644 --- a/src/backend_dispatch.rs +++ b/src/backend_dispatch.rs @@ -3,6 +3,7 @@ use crate::{backend_interface::*, backends, InitError, Rect, SoftBufferError}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; +use std::fmt; use std::num::NonZeroU32; #[cfg(any(wayland_platform, x11_platform, kms_platform))] use std::sync::Arc; @@ -56,6 +57,17 @@ macro_rules! make_dispatch { } } + impl fmt::Debug for ContextDispatch { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.fmt(f), + )* + } + } + } + #[allow(clippy::large_enum_variant)] // it's boxed anyways pub(crate) enum SurfaceDispatch<$dgen, $wgen> { $( @@ -117,6 +129,17 @@ macro_rules! make_dispatch { } } + impl fmt::Debug for SurfaceDispatch { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.fmt(f), + )* + } + } + } + pub(crate) enum BufferDispatch<'a, $dgen, $wgen> { $( $(#[$attr])* @@ -192,6 +215,17 @@ macro_rules! make_dispatch { } } } + + impl fmt::Debug for BufferDispatch<'_, D, W> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.fmt(f), + )* + } + } + } }; } diff --git a/src/backends/android.rs b/src/backends/android.rs index 916cde8..67f8926 100644 --- a/src/backends/android.rs +++ b/src/backends/android.rs @@ -12,9 +12,10 @@ use raw_window_handle::AndroidNdkWindowHandle; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; use crate::error::InitError; -use crate::{BufferInterface, Rect, SoftBufferError, SurfaceInterface}; +use crate::{util, BufferInterface, Rect, SoftBufferError, SurfaceInterface}; /// The handle to a window for software buffering. +#[derive(Debug)] pub struct AndroidImpl { native_window: NativeWindow, window: W, @@ -102,7 +103,7 @@ impl SurfaceInterface for Android Ok(BufferImpl { native_window_buffer, - buffer, + buffer: util::PixelBuffer(buffer), marker: PhantomData, }) } @@ -113,9 +114,10 @@ impl SurfaceInterface for Android } } +#[derive(Debug)] pub struct BufferImpl<'a, D: ?Sized, W> { native_window_buffer: NativeWindowBufferLockGuard<'a>, - buffer: Vec, + buffer: util::PixelBuffer, marker: PhantomData<(&'a D, &'a W)>, } diff --git a/src/backends/cg.rs b/src/backends/cg.rs index 6e8999d..a5dc52a 100644 --- a/src/backends/cg.rs +++ b/src/backends/cg.rs @@ -1,7 +1,7 @@ //! Softbuffer implementation using CoreGraphics. use crate::backend_interface::*; use crate::error::InitError; -use crate::{Rect, SoftBufferError}; +use crate::{util, Rect, SoftBufferError}; use objc2::rc::Retained; use objc2::runtime::{AnyObject, Bool}; use objc2::{define_class, msg_send, AllocAnyThread, DefinedClass, MainThreadMarker, Message}; @@ -29,6 +29,7 @@ define_class!( #[unsafe(super(NSObject))] #[name = "SoftbufferObserver"] #[ivars = SendCALayer] + #[derive(Debug)] struct Observer; /// NSKeyValueObserving @@ -93,6 +94,7 @@ impl Observer { } } +#[derive(Debug)] pub struct CGImpl { /// Our layer. layer: SendCALayer, @@ -258,15 +260,16 @@ impl SurfaceInterface for CGImpl< fn buffer_mut(&mut self) -> Result, SoftBufferError> { Ok(BufferImpl { - buffer: vec![0; self.width * self.height].into(), + buffer: util::PixelBuffer(vec![0; self.width * self.height]), imp: self, }) } } +#[derive(Debug)] pub struct BufferImpl<'a, D, W> { imp: &'a mut CGImpl, - buffer: Box<[u32]>, + buffer: util::PixelBuffer, } impl BufferInterface for BufferImpl<'_, D, W> { @@ -306,7 +309,7 @@ impl BufferInterface for BufferImpl<'_, let data_provider = { let len = self.buffer.len() * size_of::(); - let buffer: *mut [u32] = Box::into_raw(self.buffer); + let buffer: *mut [u32] = Box::into_raw(self.buffer.0.into_boxed_slice()); // Convert slice pointer to thin pointer. let data_ptr = buffer.cast::(); @@ -363,6 +366,7 @@ impl BufferInterface for BufferImpl<'_, } } +#[derive(Debug)] struct SendCALayer(Retained); // SAFETY: CALayer is dubiously thread safe, like most things in Core Animation. diff --git a/src/backends/kms.rs b/src/backends/kms.rs index 3008e8c..d18d633 100644 --- a/src/backends/kms.rs +++ b/src/backends/kms.rs @@ -12,6 +12,7 @@ use drm::Device; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle}; use std::collections::HashSet; +use std::fmt; use std::marker::PhantomData; use std::num::NonZeroU32; use std::os::unix::io::{AsFd, BorrowedFd}; @@ -118,6 +119,13 @@ pub(crate) struct BufferImpl<'a, D: ?Sized, W: ?Sized> { _window: PhantomData<&'a mut W>, } +impl fmt::Debug for BufferImpl<'_, D, W> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // FIXME: Derive instead once `DumbMapping` impls `Debug`. + f.debug_struct("BufferImpl").finish_non_exhaustive() + } +} + /// The combined frame buffer and dumb buffer. #[derive(Debug)] struct SharedBuffer { diff --git a/src/backends/orbital.rs b/src/backends/orbital.rs index cea6feb..301baba 100644 --- a/src/backends/orbital.rs +++ b/src/backends/orbital.rs @@ -3,8 +3,9 @@ use raw_window_handle::{HasDisplayHandle, HasWindowHandle, OrbitalWindowHandle, use std::{cmp, marker::PhantomData, num::NonZeroU32, slice, str}; use crate::backend_interface::*; -use crate::{Rect, SoftBufferError}; +use crate::{util, Rect, SoftBufferError}; +#[derive(Debug)] struct OrbitalMap { address: usize, size: usize, @@ -55,6 +56,7 @@ impl Drop for OrbitalMap { } } +#[derive(Debug)] pub struct OrbitalImpl { handle: ThreadSafeWindowHandle, width: u32, @@ -64,6 +66,7 @@ pub struct OrbitalImpl { _display: PhantomData, } +#[derive(Debug)] struct ThreadSafeWindowHandle(OrbitalWindowHandle); unsafe impl Send for ThreadSafeWindowHandle {} unsafe impl Sync for ThreadSafeWindowHandle {} @@ -174,17 +177,23 @@ impl SurfaceInterface for Orbital .expect("failed to map orbital window"), ) } else { - Pixels::Buffer(vec![0; self.width as usize * self.height as usize]) + Pixels::Buffer(util::PixelBuffer(vec![ + 0; + self.width as usize + * self.height as usize + ])) }; Ok(BufferImpl { imp: self, pixels }) } } +#[derive(Debug)] enum Pixels { Mapping(OrbitalMap), - Buffer(Vec), + Buffer(util::PixelBuffer), } +#[derive(Debug)] pub struct BufferImpl<'a, D, W> { imp: &'a mut OrbitalImpl, pixels: Pixels, diff --git a/src/backends/wayland/buffer.rs b/src/backends/wayland/buffer.rs index 407dd74..49e641f 100644 --- a/src/backends/wayland/buffer.rs +++ b/src/backends/wayland/buffer.rs @@ -67,6 +67,7 @@ unsafe fn map_file(file: &File) -> MmapMut { unsafe { MmapMut::map_mut(file.as_raw_fd()).expect("Failed to map shared memory") } } +#[derive(Debug)] pub(super) struct WaylandBuffer { qh: QueueHandle, tempfile: File, diff --git a/src/backends/wayland/mod.rs b/src/backends/wayland/mod.rs index d497484..2d37494 100644 --- a/src/backends/wayland/mod.rs +++ b/src/backends/wayland/mod.rs @@ -20,6 +20,7 @@ use buffer::WaylandBuffer; struct State; +#[derive(Debug)] pub struct WaylandDisplayImpl { conn: Option, event_queue: Mutex>, @@ -74,6 +75,7 @@ impl Drop for WaylandDisplayImpl { } } +#[derive(Debug)] pub struct WaylandImpl { display: Arc>, surface: Option, @@ -261,6 +263,7 @@ impl Drop for WaylandImpl { } } +#[derive(Debug)] pub struct BufferImpl<'a, D: ?Sized, W> { stack: util::BorrowStack<'a, WaylandImpl, [u32]>, width: i32, diff --git a/src/backends/web.rs b/src/backends/web.rs index 3b8efa1..358e934 100644 --- a/src/backends/web.rs +++ b/src/backends/web.rs @@ -18,7 +18,7 @@ use std::num::NonZeroU32; /// Display implementation for the web platform. /// /// This just caches the document to prevent having to query it every time. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct WebDisplayImpl { document: web_sys::Document, _display: D, @@ -43,12 +43,13 @@ impl ContextInterface for WebDisplayImpl { } } +#[derive(Debug)] pub struct WebImpl { /// The handle and context to the canvas that we're drawing to. canvas: Canvas, /// The buffer that we're drawing to. - buffer: Vec, + buffer: util::PixelBuffer, /// Buffer has been presented. buffer_presented: bool, @@ -65,6 +66,7 @@ pub struct WebImpl { /// Holding canvas and context for [`HtmlCanvasElement`] or [`OffscreenCanvas`], /// since they have different types. +#[derive(Debug)] enum Canvas { Canvas { canvas: HtmlCanvasElement, @@ -82,7 +84,7 @@ impl WebImpl { Ok(Self { canvas: Canvas::Canvas { canvas, ctx }, - buffer: Vec::new(), + buffer: util::PixelBuffer(Vec::new()), buffer_presented: false, size: None, window_handle: window, @@ -98,7 +100,7 @@ impl WebImpl { Ok(Self { canvas: Canvas::OffscreenCanvas { canvas, ctx }, - buffer: Vec::new(), + buffer: util::PixelBuffer(Vec::new()), buffer_presented: false, size: None, window_handle: window, @@ -373,6 +375,7 @@ impl Canvas { } } +#[derive(Debug)] pub struct BufferImpl<'a, D, W> { imp: &'a mut WebImpl, } diff --git a/src/backends/win32.rs b/src/backends/win32.rs index 3436f55..75de23e 100644 --- a/src/backends/win32.rs +++ b/src/backends/win32.rs @@ -25,6 +25,7 @@ const ZERO_QUAD: Gdi::RGBQUAD = Gdi::RGBQUAD { rgbReserved: 0, }; +#[derive(Debug)] struct Buffer { dc: Gdi::HDC, bitmap: Gdi::HBITMAP, @@ -135,6 +136,7 @@ impl Buffer { } /// The handle to a window for software buffering. +#[derive(Debug)] pub struct Win32Impl { /// The window handle. window: OnlyUsedFromOrigin, @@ -281,6 +283,7 @@ impl SurfaceInterface for Win32Im } } +#[derive(Debug)] pub struct BufferImpl<'a, D, W>(&'a mut Win32Impl); impl BufferInterface for BufferImpl<'_, D, W> { @@ -468,6 +471,7 @@ impl Command { } } +#[derive(Debug)] struct OnlyUsedFromOrigin(T); unsafe impl Send for OnlyUsedFromOrigin {} diff --git a/src/backends/x11.rs b/src/backends/x11.rs index f142971..711b0b7 100644 --- a/src/backends/x11.rs +++ b/src/backends/x11.rs @@ -7,7 +7,7 @@ use crate::backend_interface::*; use crate::error::{InitError, SwResultExt}; -use crate::{Rect, SoftBufferError}; +use crate::{util, Rect, SoftBufferError}; use raw_window_handle::{ HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle, @@ -36,6 +36,7 @@ use x11rb::protocol::shm::{self, ConnectionExt as _}; use x11rb::protocol::xproto::{self, ConnectionExt as _, ImageOrder, VisualClass, Visualid}; use x11rb::xcb_ffi::XCBConnection; +#[derive(Debug)] pub struct X11DisplayImpl { /// The handle to the XCB connection. connection: Option, @@ -125,6 +126,7 @@ impl X11DisplayImpl { } /// The handle to an X11 drawing context. +#[derive(Debug)] pub struct X11Impl { /// X display this window belongs to. display: Arc>, @@ -155,14 +157,16 @@ pub struct X11Impl { } /// The buffer that is being drawn to. +#[derive(Debug)] enum Buffer { /// A buffer implemented using shared memory to prevent unnecessary copying. Shm(ShmBuffer), /// A normal buffer that we send over the wire. - Wire(Vec), + Wire(util::PixelBuffer), } +#[derive(Debug)] struct ShmBuffer { /// The shared memory segment, paired with its ID. seg: Option<(ShmSegment, shm::Seg)>, @@ -285,7 +289,7 @@ impl SurfaceInterface fo }) } else { // SHM is not available. - Buffer::Wire(Vec::new()) + Buffer::Wire(util::PixelBuffer(Vec::new())) }; Ok(Self { @@ -383,6 +387,7 @@ impl SurfaceInterface fo } } +#[derive(Debug)] pub struct BufferImpl<'a, D: ?Sized, W: ?Sized>(&'a mut X11Impl); impl BufferInterface @@ -689,6 +694,7 @@ impl ShmBuffer { } } +#[derive(Debug)] struct ShmSegment { id: File, ptr: NonNull, @@ -795,7 +801,10 @@ impl Drop for X11DisplayImpl { impl Drop for X11Impl { fn drop(&mut self) { // If we used SHM, make sure it's detached from the server. - if let Buffer::Shm(mut shm) = mem::replace(&mut self.buffer, Buffer::Wire(Vec::new())) { + if let Buffer::Shm(mut shm) = mem::replace( + &mut self.buffer, + Buffer::Wire(util::PixelBuffer(Vec::new())), + ) { // If we were in the middle of processing a buffer, wait for it to finish. shm.finish_wait(self.display.connection()).ok(); diff --git a/src/lib.rs b/src/lib.rs index 03246c0..bfababc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,7 +30,7 @@ pub use backends::web::SurfaceExtWeb; /// An instance of this struct contains the platform-specific data that must be managed in order to /// write to a window on that platform. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Context { /// The inner static dispatch object. context_impl: ContextDispatch, @@ -73,6 +73,7 @@ pub struct Rect { } /// A surface for drawing to a window with software buffers. +#[derive(Debug)] pub struct Surface { /// This is boxed so that `Surface` is the same size on every platform. surface_impl: Box>, @@ -200,6 +201,7 @@ impl HasWindowHandle for Surface /// /// Buffer copies an channel swizzling happen on: /// - Android +#[derive(Debug)] pub struct Buffer<'a, D, W> { buffer_impl: BufferDispatch<'a, D, W>, _marker: PhantomData<(Arc, Cell<()>)>, diff --git a/src/util.rs b/src/util.rs index de46e3f..572de5e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,7 +2,9 @@ #![allow(dead_code)] use std::cmp; +use std::fmt; use std::num::NonZeroU32; +use std::ops; use crate::Rect; use crate::SoftBufferError; @@ -50,6 +52,14 @@ impl<'a, T: 'a + ?Sized, U: 'a + ?Sized> BorrowStack<'a, T, U> { } } +impl<'a, T: 'a + ?Sized, U: 'a + ?Sized + fmt::Debug> fmt::Debug for BorrowStack<'a, T, U> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BorrowStack") + .field("member", &self.member()) + .finish_non_exhaustive() + } +} + /// Calculates the smallest `Rect` necessary to represent all damaged `Rect`s. pub(crate) fn union_damage(damage: &[Rect]) -> Option { struct Region { @@ -85,6 +95,29 @@ pub(crate) fn union_damage(damage: &[Rect]) -> Option { }) } +/// A wrapper around a `Vec` of pixels that doesn't print the whole buffer on `Debug`. +#[derive(PartialEq, Eq, Hash, Clone)] +pub(crate) struct PixelBuffer(pub Vec); + +impl fmt::Debug for PixelBuffer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PixelBuffer").finish_non_exhaustive() + } +} + +impl ops::Deref for PixelBuffer { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ops::DerefMut for PixelBuffer { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + #[cfg(test)] mod tests { use super::*;