diff --git a/src/backend_dispatch.rs b/src/backend_dispatch.rs index 01c25c1..e23a0c2 100644 --- a/src/backend_dispatch.rs +++ b/src/backend_dispatch.rs @@ -1,6 +1,6 @@ //! Implements `buffer_interface::*` traits for enums dispatching to backends -use crate::{backend_interface::*, backends, Rect, SoftBufferError}; +use crate::{backend_interface::*, backends, InitError, Rect, SoftBufferError}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use std::num::NonZeroU32; @@ -35,6 +35,26 @@ macro_rules! make_dispatch { } } + impl ContextInterface for ContextDispatch { + fn new(mut display: D) -> Result> + where + D: Sized, + { + $( + $(#[$attr])* + match <$context_inner as ContextInterface>::new(display) { + Ok(x) => { + return Ok(Self::$name(x)); + } + Err(InitError::Unsupported(d)) => display = d, + Err(InitError::Failure(f)) => return Err(InitError::Failure(f)), + } + )* + + Err(InitError::Unsupported(display)) + } + } + #[allow(clippy::large_enum_variant)] // it's boxed anyways pub(crate) enum SurfaceDispatch<$dgen, $wgen> { $( @@ -43,9 +63,22 @@ macro_rules! make_dispatch { )* } - impl SurfaceInterface for SurfaceDispatch { + impl SurfaceInterface for SurfaceDispatch { + type Context = ContextDispatch; type Buffer<'a> = BufferDispatch<'a, D, W> where Self: 'a; + fn new(window: W, display: &Self::Context) -> Result> + where + W: Sized, + Self: Sized { + match display { + $( + $(#[$attr])* + ContextDispatch::$name(inner) => Ok(Self::$name(<$surface_inner>::new(window, inner)?)), + )* + } + } + fn window(&self) -> &W { match self { $( diff --git a/src/backend_interface.rs b/src/backend_interface.rs index 8f16de2..13e3555 100644 --- a/src/backend_interface.rs +++ b/src/backend_interface.rs @@ -1,15 +1,27 @@ //! Interface implemented by backends -use crate::{Rect, SoftBufferError}; +use crate::{InitError, Rect, SoftBufferError}; -use raw_window_handle::HasWindowHandle; +use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use std::num::NonZeroU32; -pub(crate) trait SurfaceInterface { +pub(crate) trait ContextInterface { + fn new(display: D) -> Result> + where + D: Sized, + Self: Sized; +} + +pub(crate) trait SurfaceInterface { + type Context: ContextInterface; type Buffer<'a>: BufferInterface where Self: 'a; + fn new(window: W, context: &Self::Context) -> Result> + where + W: Sized, + Self: Sized; /// Get the inner window handle. fn window(&self) -> &W; /// Resize the internal buffer to the given width and height. diff --git a/src/backends/cg.rs b/src/backends/cg.rs index 9ad62e6..3757880 100644 --- a/src/backends/cg.rs +++ b/src/backends/cg.rs @@ -35,8 +35,11 @@ pub struct CGImpl { _display: PhantomData, } -impl CGImpl { - pub(crate) fn new(window_src: W) -> Result> { +impl SurfaceInterface for CGImpl { + type Context = D; + type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; + + fn new(window_src: W, _display: &D) -> Result> { let raw = window_src.window_handle()?.as_raw(); let handle = match raw { RawWindowHandle::AppKit(handle) => handle, @@ -66,10 +69,6 @@ impl CGImpl { window_handle: window_src, }) } -} - -impl SurfaceInterface for CGImpl { - type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; #[inline] fn window(&self) -> &W { diff --git a/src/backends/kms.rs b/src/backends/kms.rs index a523976..13c040b 100644 --- a/src/backends/kms.rs +++ b/src/backends/kms.rs @@ -38,8 +38,11 @@ impl AsFd for KmsDisplayImpl { impl Device for KmsDisplayImpl {} impl CtrlDevice for KmsDisplayImpl {} -impl KmsDisplayImpl { - pub(crate) fn new(display: D) -> Result> { +impl ContextInterface for Rc> { + fn new(display: D) -> Result> + where + D: Sized, + { let fd = match display.display_handle()?.as_raw() { RawDisplayHandle::Drm(drm) => drm.fd, _ => return Err(InitError::Unsupported(display)), @@ -51,10 +54,10 @@ impl KmsDisplayImpl { // SAFETY: Invariants guaranteed by the user. let fd = unsafe { BorrowedFd::borrow_raw(fd) }; - Ok(KmsDisplayImpl { + Ok(Rc::new(KmsDisplayImpl { fd, _display: display, - }) + })) } } @@ -135,9 +138,12 @@ struct SharedBuffer { age: u8, } -impl KmsImpl { +impl SurfaceInterface for KmsImpl { + type Context = Rc>; + type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; + /// Create a new KMS backend. - pub(crate) fn new(window: W, display: Rc>) -> Result> { + fn new(window: W, display: &Rc>) -> Result> { // Make sure that the window handle is valid. let plane_handle = match window.window_handle()?.as_raw() { RawWindowHandle::Drm(drm) => match NonZeroU32::new(drm.plane) { @@ -199,15 +205,11 @@ impl KmsImpl { Ok(Self { crtc, connectors, - display, + display: display.clone(), buffer: None, window_handle: window, }) } -} - -impl SurfaceInterface for KmsImpl { - type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; #[inline] fn window(&self) -> &W { diff --git a/src/backends/mod.rs b/src/backends/mod.rs index 35bc66e..8402b44 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -1,3 +1,6 @@ +use crate::{ContextInterface, InitError}; +use raw_window_handle::HasDisplayHandle; + #[cfg(target_os = "macos")] pub(crate) mod cg; #[cfg(kms_platform)] @@ -12,3 +15,9 @@ pub(crate) mod web; pub(crate) mod win32; #[cfg(x11_platform)] pub(crate) mod x11; + +impl ContextInterface for D { + fn new(display: D) -> Result> { + Ok(display) + } +} diff --git a/src/backends/orbital.rs b/src/backends/orbital.rs index dae5d9e..7fb5c16 100644 --- a/src/backends/orbital.rs +++ b/src/backends/orbital.rs @@ -65,23 +65,6 @@ pub struct OrbitalImpl { } impl OrbitalImpl { - pub(crate) fn new(window: W) -> Result> { - let raw = window.window_handle()?.as_raw(); - let handle = match raw { - RawWindowHandle::Orbital(handle) => handle, - _ => return Err(InitError::Unsupported(window)), - }; - - Ok(Self { - handle, - width: 0, - height: 0, - presented: false, - window_handle: window, - _display: PhantomData, - }) - } - fn window_fd(&self) -> usize { self.handle.window.as_ptr() as usize } @@ -139,9 +122,27 @@ impl OrbitalImpl { } } -impl SurfaceInterface for OrbitalImpl { +impl SurfaceInterface for OrbitalImpl { + type Context = D; type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; + fn new(window: W, _display: &D) -> Result> { + let raw = window.window_handle()?.as_raw(); + let handle = match raw { + RawWindowHandle::Orbital(handle) => handle, + _ => return Err(InitError::Unsupported(window)), + }; + + Ok(Self { + handle, + width: 0, + height: 0, + presented: false, + window_handle: window, + _display: PhantomData, + }) + } + #[inline] fn window(&self) -> &W { &self.window_handle diff --git a/src/backends/wayland/mod.rs b/src/backends/wayland/mod.rs index bf95687..32be3d4 100644 --- a/src/backends/wayland/mod.rs +++ b/src/backends/wayland/mod.rs @@ -35,7 +35,13 @@ pub struct WaylandDisplayImpl { } impl WaylandDisplayImpl { - pub(crate) fn new(display: D) -> Result> + fn conn(&self) -> &Connection { + self.conn.as_ref().unwrap() + } +} + +impl ContextInterface for Rc> { + fn new(display: D) -> Result> where D: Sized, { @@ -53,17 +59,13 @@ impl WaylandDisplayImpl { let shm: wl_shm::WlShm = globals .bind(&qh, 1..=1, ()) .swbuf_err("Failed to instantiate Wayland Shm")?; - Ok(Self { + Ok(Rc::new(WaylandDisplayImpl { conn: Some(conn), event_queue: RefCell::new(event_queue), qh, shm, _display: display, - }) - } - - fn conn(&self) -> &Connection { - self.conn.as_ref().unwrap() + })) } } @@ -88,32 +90,6 @@ pub struct WaylandImpl { } impl WaylandImpl { - pub(crate) fn new(window: W, display: Rc>) -> Result> { - // Get the raw Wayland window. - let raw = window.window_handle()?.as_raw(); - let wayland_handle = match raw { - RawWindowHandle::Wayland(w) => w.surface, - _ => return Err(InitError::Unsupported(window)), - }; - - let surface_id = unsafe { - ObjectId::from_ptr( - wl_surface::WlSurface::interface(), - wayland_handle.as_ptr().cast(), - ) - } - .swbuf_err("Failed to create proxy for surface ID.")?; - let surface = wl_surface::WlSurface::from_id(display.conn(), surface_id) - .swbuf_err("Failed to create proxy for surface ID.")?; - Ok(Self { - display, - surface: Some(surface), - buffers: Default::default(), - size: None, - window_handle: window, - }) - } - fn surface(&self) -> &wl_surface::WlSurface { self.surface.as_ref().unwrap() } @@ -166,9 +142,38 @@ impl WaylandImpl { } } -impl SurfaceInterface for WaylandImpl { +impl SurfaceInterface + for WaylandImpl +{ + type Context = Rc>; type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; + fn new(window: W, display: &Rc>) -> Result> { + // Get the raw Wayland window. + let raw = window.window_handle()?.as_raw(); + let wayland_handle = match raw { + RawWindowHandle::Wayland(w) => w.surface, + _ => return Err(InitError::Unsupported(window)), + }; + + let surface_id = unsafe { + ObjectId::from_ptr( + wl_surface::WlSurface::interface(), + wayland_handle.as_ptr().cast(), + ) + } + .swbuf_err("Failed to create proxy for surface ID.")?; + let surface = wl_surface::WlSurface::from_id(display.conn(), surface_id) + .swbuf_err("Failed to create proxy for surface ID.")?; + Ok(Self { + display: display.clone(), + surface: Some(surface), + buffers: Default::default(), + size: None, + window_handle: window, + }) + } + #[inline] fn window(&self) -> &W { &self.window_handle diff --git a/src/backends/web.rs b/src/backends/web.rs index 5b631a8..2b8e9b5 100644 --- a/src/backends/web.rs +++ b/src/backends/web.rs @@ -24,8 +24,8 @@ pub struct WebDisplayImpl { _display: D, } -impl WebDisplayImpl { - pub(crate) fn new(display: D) -> Result> { +impl ContextInterface for WebDisplayImpl { + fn new(display: D) -> Result> { let raw = display.display_handle()?.as_raw(); match raw { RawDisplayHandle::Web(..) => {} @@ -78,35 +78,6 @@ enum Canvas { } impl WebImpl { - pub(crate) fn new(display: &WebDisplayImpl, window: W) -> Result> { - let raw = window.window_handle()?.as_raw(); - let canvas: HtmlCanvasElement = match raw { - RawWindowHandle::Web(handle) => { - display - .document - .query_selector(&format!("canvas[data-raw-handle=\"{}\"]", handle.id)) - // `querySelector` only throws an error if the selector is invalid. - .unwrap() - .swbuf_err("No canvas found with the given id")? - // We already made sure this was a canvas in `querySelector`. - .unchecked_into() - } - RawWindowHandle::WebCanvas(handle) => { - let value: &JsValue = unsafe { handle.obj.cast().as_ref() }; - value.clone().unchecked_into() - } - RawWindowHandle::WebOffscreenCanvas(handle) => { - let value: &JsValue = unsafe { handle.obj.cast().as_ref() }; - let canvas: OffscreenCanvas = value.clone().unchecked_into(); - - return Self::from_offscreen_canvas(canvas, window).map_err(InitError::Failure); - } - _ => return Err(InitError::Unsupported(window)), - }; - - Self::from_canvas(canvas, window).map_err(InitError::Failure) - } - fn from_canvas(canvas: HtmlCanvasElement, window: W) -> Result { let ctx = Self::resolve_ctx(canvas.get_context("2d").ok(), "CanvasRenderingContext2d")?; @@ -234,9 +205,39 @@ impl WebImpl { } } -impl SurfaceInterface for WebImpl { +impl SurfaceInterface for WebImpl { + type Context = WebDisplayImpl; type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; + fn new(window: W, display: &WebDisplayImpl) -> Result> { + let raw = window.window_handle()?.as_raw(); + let canvas: HtmlCanvasElement = match raw { + RawWindowHandle::Web(handle) => { + display + .document + .query_selector(&format!("canvas[data-raw-handle=\"{}\"]", handle.id)) + // `querySelector` only throws an error if the selector is invalid. + .unwrap() + .swbuf_err("No canvas found with the given id")? + // We already made sure this was a canvas in `querySelector`. + .unchecked_into() + } + RawWindowHandle::WebCanvas(handle) => { + let value: &JsValue = unsafe { handle.obj.cast().as_ref() }; + value.clone().unchecked_into() + } + RawWindowHandle::WebOffscreenCanvas(handle) => { + let value: &JsValue = unsafe { handle.obj.cast().as_ref() }; + let canvas: OffscreenCanvas = value.clone().unchecked_into(); + + return Self::from_offscreen_canvas(canvas, window).map_err(InitError::Failure); + } + _ => return Err(InitError::Unsupported(window)), + }; + + Self::from_canvas(canvas, window).map_err(InitError::Failure) + } + /// Get the inner window handle. #[inline] fn window(&self) -> &W { diff --git a/src/backends/win32.rs b/src/backends/win32.rs index a4d9827..120b55a 100644 --- a/src/backends/win32.rs +++ b/src/backends/win32.rs @@ -159,8 +159,37 @@ struct BitmapInfo { } impl Win32Impl { + 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(()) + } +} + +impl SurfaceInterface for Win32Impl { + type Context = D; + type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; + /// Create a new `Win32Impl` from a `Win32WindowHandle`. - pub(crate) fn new(window: W) -> Result> { + fn new(window: W, _display: &D) -> Result> { let raw = window.window_handle()?.as_raw(); let handle = match raw { RawWindowHandle::Win32(handle) => handle, @@ -190,34 +219,6 @@ impl Win32Impl { }) } - 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(()) - } -} - -impl SurfaceInterface for Win32Impl { - type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; - #[inline] fn window(&self) -> &W { &self.handle diff --git a/src/backends/x11.rs b/src/backends/x11.rs index ed21486..16bdab4 100644 --- a/src/backends/x11.rs +++ b/src/backends/x11.rs @@ -54,9 +54,9 @@ pub struct X11DisplayImpl { _display: D, } -impl X11DisplayImpl { +impl ContextInterface for Rc> { /// Create a new `X11DisplayImpl`. - pub(crate) fn new(display: D) -> Result> + fn new(display: D) -> Result> where D: Sized, { @@ -107,12 +107,12 @@ impl X11DisplayImpl { let supported_visuals = supported_visuals(&connection); - Ok(Self { + Ok(Rc::new(X11DisplayImpl { connection: Some(connection), is_shm_available, supported_visuals, _display: display, - }) + })) } } @@ -182,9 +182,12 @@ struct ShmBuffer { done_processing: Option, } -impl X11Impl { +impl SurfaceInterface for X11Impl { + type Context = Rc>; + type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; + /// Create a new `X11Impl` from a `HasWindowHandle`. - pub(crate) fn new(window_src: W, display: Rc>) -> Result> { + fn new(window_src: W, display: &Rc>) -> Result> { // Get the underlying raw window handle. let raw = window_src.window_handle()?.as_raw(); let window_handle = match raw { @@ -285,7 +288,7 @@ impl X11Impl { }; Ok(Self { - display, + display: display.clone(), window, gc, depth: geometry_reply.depth, @@ -296,10 +299,6 @@ impl X11Impl { window_handle: window_src, }) } -} - -impl SurfaceInterface for X11Impl { - type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; #[inline] fn window(&self) -> &W { diff --git a/src/lib.rs b/src/lib.rs index ec29853..13e97ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,8 +19,6 @@ mod util; use std::marker::PhantomData; use std::num::NonZeroU32; use std::ops; -#[cfg(any(wayland_platform, x11_platform, kms_platform))] -use std::rc::Rc; use error::InitError; pub use error::SoftBufferError; @@ -41,43 +39,21 @@ pub struct Context { impl Context { /// Creates a new instance of this struct, using the provided display. - pub fn new(mut dpy: D) -> Result { - macro_rules! try_init { - ($imp:ident, $x:ident => $make_it:expr) => {{ - let $x = dpy; - match { $make_it } { - Ok(x) => { - return Ok(Self { - context_impl: ContextDispatch::$imp(x), - _marker: PhantomData, - }) - } - Err(InitError::Unsupported(d)) => dpy = d, - Err(InitError::Failure(f)) => return Err(f), - } - }}; + pub fn new(display: D) -> Result { + match ContextDispatch::new(display) { + Ok(context_impl) => Ok(Self { + context_impl, + _marker: PhantomData, + }), + Err(InitError::Unsupported(display)) => { + let raw = display.display_handle()?.as_raw(); + Err(SoftBufferError::UnsupportedDisplayPlatform { + human_readable_display_platform_name: display_handle_type_name(&raw), + display_handle: raw, + }) + } + Err(InitError::Failure(f)) => Err(f), } - - #[cfg(x11_platform)] - try_init!(X11, display => backends::x11::X11DisplayImpl::new(display).map(Rc::new)); - #[cfg(wayland_platform)] - try_init!(Wayland, display => backends::wayland::WaylandDisplayImpl::new(display).map(Rc::new)); - #[cfg(kms_platform)] - try_init!(Kms, display => backends::kms::KmsDisplayImpl::new(display).map(Rc::new)); - #[cfg(target_os = "windows")] - try_init!(Win32, display => Ok(display)); - #[cfg(target_os = "macos")] - try_init!(CG, display => Ok(display)); - #[cfg(target_arch = "wasm32")] - try_init!(Web, display => backends::web::WebDisplayImpl::new(display)); - #[cfg(target_os = "redox")] - try_init!(Orbital, display => Ok(display)); - - let raw = dpy.display_handle()?.as_raw(); - Err(SoftBufferError::UnsupportedDisplayPlatform { - human_readable_display_platform_name: display_handle_type_name(&raw), - display_handle: raw, - }) } } @@ -104,58 +80,21 @@ pub struct Surface { impl Surface { /// Creates a new surface for the context for the provided window. pub fn new(context: &Context, window: W) -> Result { - macro_rules! leap { - ($e:expr) => {{ - match ($e) { - Ok(x) => x, - Err(InitError::Unsupported(window)) => { - let raw = window.window_handle()?.as_raw(); - return Err(SoftBufferError::UnsupportedWindowPlatform { - human_readable_window_platform_name: window_handle_type_name(&raw), - human_readable_display_platform_name: context - .context_impl - .variant_name(), - window_handle: raw, - }); - } - Err(InitError::Failure(f)) => return Err(f), - } - }}; - } - - let imple = match &context.context_impl { - #[cfg(x11_platform)] - ContextDispatch::X11(xcb_display_handle) => SurfaceDispatch::X11(leap!( - backends::x11::X11Impl::new(window, xcb_display_handle.clone()) - )), - #[cfg(wayland_platform)] - ContextDispatch::Wayland(wayland_display_impl) => SurfaceDispatch::Wayland(leap!( - backends::wayland::WaylandImpl::new(window, wayland_display_impl.clone()) - )), - #[cfg(kms_platform)] - ContextDispatch::Kms(kms_display_impl) => SurfaceDispatch::Kms(leap!( - backends::kms::KmsImpl::new(window, kms_display_impl.clone()) - )), - #[cfg(target_os = "windows")] - ContextDispatch::Win32(_) => { - SurfaceDispatch::Win32(leap!(backends::win32::Win32Impl::new(window))) + match SurfaceDispatch::new(window, &context.context_impl) { + Ok(surface_dispatch) => Ok(Self { + surface_impl: Box::new(surface_dispatch), + _marker: PhantomData, + }), + Err(InitError::Unsupported(window)) => { + let raw = window.window_handle()?.as_raw(); + Err(SoftBufferError::UnsupportedWindowPlatform { + human_readable_window_platform_name: window_handle_type_name(&raw), + human_readable_display_platform_name: context.context_impl.variant_name(), + window_handle: raw, + }) } - #[cfg(target_os = "macos")] - ContextDispatch::CG(_) => SurfaceDispatch::CG(leap!(backends::cg::CGImpl::new(window))), - #[cfg(target_arch = "wasm32")] - ContextDispatch::Web(web_display_impl) => { - SurfaceDispatch::Web(leap!(backends::web::WebImpl::new(web_display_impl, window))) - } - #[cfg(target_os = "redox")] - ContextDispatch::Orbital(_) => { - SurfaceDispatch::Orbital(leap!(backends::orbital::OrbitalImpl::new(window))) - } - }; - - Ok(Self { - surface_impl: Box::new(imple), - _marker: PhantomData, - }) + Err(InitError::Failure(f)) => Err(f), + } } /// Get a reference to the underlying window handle.