diff --git a/src/cg.rs b/src/cg.rs index f38d2f2..7703b43 100644 --- a/src/cg.rs +++ b/src/cg.rs @@ -1,4 +1,4 @@ -use crate::{GraphicsContextImpl, SwBufError}; +use crate::SwBufError; use raw_window_handle::AppKitWindowHandle; use core_graphics::base::{kCGBitmapByteOrder32Little, kCGImageAlphaNoneSkipFirst, kCGRenderingIntentDefault}; use core_graphics::color_space::CGColorSpace; @@ -32,10 +32,8 @@ impl CGImpl { let _: () = msg_send![subview, release]; // releases subview (-1) = 1 Ok(Self{layer}) } -} -impl GraphicsContextImpl for CGImpl { - unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { + pub(crate) unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { let color_space = CGColorSpace::create_device_rgb(); let data = std::slice::from_raw_parts( buffer.as_ptr() as *const u8, diff --git a/src/lib.rs b/src/lib.rs index f28200b..6b17e67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,54 @@ use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandl /// An instance of this struct contains the platform-specific data that must be managed in order to /// write to a window on that platform. pub struct GraphicsContext { - graphics_context_impl: Box, + /// The inner static dispatch object. + /// + /// This is boxed so that `GraphicsContext` is the same size on every platform, which should + /// hopefully prevent surprises. + graphics_context_impl: Box +} + +/// A macro for creating the enum used to statically dispatch to the platform-specific implementation. +macro_rules! make_dispatch { + ( + $( + $(#[$attr:meta])* + $name: ident ($inner_ty: ty), + )* + ) => { + enum Dispatch { + $( + $(#[$attr])* + $name($inner_ty), + )* + } + + impl Dispatch { + unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.set_buffer(buffer, width, height), + )* + } + } + } + }; +} + +make_dispatch! { + #[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd")))] + X11(x11::X11Impl), + #[cfg(all(feature = "wayland", any(target_os = "linux", target_os = "freebsd")))] + Wayland(wayland::WaylandImpl), + #[cfg(target_os = "windows")] + Win32(win32::Win32Impl), + #[cfg(target_os = "macos")] + CG(cg::CGImpl), + #[cfg(target_arch = "wasm32")] + Web(web::WebImpl), + #[cfg(target_os = "redox")] + Orbital(orbital::OrbitalImpl), } impl GraphicsContext { @@ -48,19 +95,19 @@ impl GraphicsContext { /// - Ensure that the provided handles are valid to draw a 2D buffer to, and are valid for the /// lifetime of the GraphicsContext pub unsafe fn from_raw(raw_window_handle: RawWindowHandle, raw_display_handle: RawDisplayHandle) -> Result { - let imple: Box = match (raw_window_handle, raw_display_handle) { + let imple: Dispatch = match (raw_window_handle, raw_display_handle) { #[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd")))] - (RawWindowHandle::Xlib(xlib_window_handle), RawDisplayHandle::Xlib(xlib_display_handle)) => Box::new(x11::X11Impl::new(xlib_window_handle, xlib_display_handle)?), + (RawWindowHandle::Xlib(xlib_window_handle), RawDisplayHandle::Xlib(xlib_display_handle)) => Dispatch::X11(x11::X11Impl::new(xlib_window_handle, xlib_display_handle)?), #[cfg(all(feature = "wayland", any(target_os = "linux", target_os = "freebsd")))] - (RawWindowHandle::Wayland(wayland_window_handle), RawDisplayHandle::Wayland(wayland_display_handle)) => Box::new(wayland::WaylandImpl::new(wayland_window_handle, wayland_display_handle)?), + (RawWindowHandle::Wayland(wayland_window_handle), RawDisplayHandle::Wayland(wayland_display_handle)) => Dispatch::Wayland(wayland::WaylandImpl::new(wayland_window_handle, wayland_display_handle)?), #[cfg(target_os = "windows")] - (RawWindowHandle::Win32(win32_handle), _) => Box::new(win32::Win32Impl::new(&win32_handle)?), + (RawWindowHandle::Win32(win32_handle), _) => Dispatch::Win32(win32::Win32Impl::new(&win32_handle)?), #[cfg(target_os = "macos")] - (RawWindowHandle::AppKit(appkit_handle), _) => Box::new(cg::CGImpl::new(appkit_handle)?), + (RawWindowHandle::AppKit(appkit_handle), _) => Dispatch::CG(cg::CGImpl::new(appkit_handle)?), #[cfg(target_arch = "wasm32")] - (RawWindowHandle::Web(web_handle), _) => Box::new(web::WebImpl::new(web_handle)?), + (RawWindowHandle::Web(web_handle), _) => Dispatch::Web(web::WebImpl::new(web_handle)?), #[cfg(target_os = "redox")] - (RawWindowHandle::Orbital(orbital_handle), _) => Box::new(orbital::OrbitalImpl::new(orbital_handle)?), + (RawWindowHandle::Orbital(orbital_handle), _) => Dispatch::Orbital(orbital::OrbitalImpl::new(orbital_handle)?), (unimplemented_window_handle, unimplemented_display_handle) => return Err(SwBufError::UnsupportedPlatform { human_readable_window_platform_name: window_handle_type_name(&unimplemented_window_handle), human_readable_display_platform_name: display_handle_type_name(&unimplemented_display_handle), @@ -70,7 +117,7 @@ impl GraphicsContext { }; Ok(Self { - graphics_context_impl: imple, + graphics_context_impl: Box::new(imple) }) } @@ -109,10 +156,6 @@ impl GraphicsContext { } } -trait GraphicsContextImpl { - unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16); -} - fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str { match handle { RawWindowHandle::Xlib(_) => "Xlib", diff --git a/src/orbital.rs b/src/orbital.rs index ab0fcde..e678e0a 100644 --- a/src/orbital.rs +++ b/src/orbital.rs @@ -5,7 +5,6 @@ use std::{ str, }; -use crate::GraphicsContextImpl; use crate::SwBufError; struct OrbitalMap { @@ -49,10 +48,8 @@ impl OrbitalImpl { pub fn new(handle: OrbitalWindowHandle) -> Result { Ok(Self { handle }) } -} -impl GraphicsContextImpl for OrbitalImpl { - unsafe fn set_buffer(&mut self, buffer: &[u32], width_u16: u16, height_u16: u16) { + pub(crate) unsafe fn set_buffer(&mut self, buffer: &[u32], width_u16: u16, height_u16: u16) { let window_fd = self.handle.window as usize; // Read the current width and size diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 84df8d3..8f20fdc 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -1,4 +1,4 @@ -use crate::{error::unwrap, GraphicsContextImpl, SwBufError}; +use crate::{error::unwrap, SwBufError}; use raw_window_handle::{WaylandDisplayHandle, WaylandWindowHandle}; use std::collections::VecDeque; use wayland_client::{ @@ -78,10 +78,8 @@ impl WaylandImpl { self.buffers.push_back(buffer); self.buffers.back().unwrap() } -} -impl GraphicsContextImpl for WaylandImpl { - unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { + pub(super) unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { let _ = self.event_queue.dispatch_pending(&mut State); let surface = self.surface.clone(); diff --git a/src/web.rs b/src/web.rs index d12a61f..ce7ec20 100644 --- a/src/web.rs +++ b/src/web.rs @@ -5,7 +5,6 @@ use web_sys::CanvasRenderingContext2d; use web_sys::HtmlCanvasElement; use web_sys::ImageData; -use crate::GraphicsContextImpl; use crate::SwBufError; pub struct WebImpl { @@ -60,10 +59,8 @@ impl WebImpl { Ok(Self { canvas, ctx }) } -} -impl GraphicsContextImpl for WebImpl { - unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { + pub(crate) unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { self.canvas.set_width(width.into()); self.canvas.set_height(height.into()); diff --git a/src/win32.rs b/src/win32.rs index 9d5bf45..c38afc6 100644 --- a/src/win32.rs +++ b/src/win32.rs @@ -1,12 +1,12 @@ //! Implementation of software buffering for Windows. -//! +//! //! This module converts the input buffer into a bitmap and then stretches it to the window. -use crate::{GraphicsContextImpl, SwBufError}; +use crate::SwBufError; use raw_window_handle::Win32WindowHandle; -use std::mem; use std::io; +use std::mem; use std::os::raw::c_int; use windows_sys::Win32::Foundation::HWND; @@ -33,9 +33,9 @@ struct BitmapInfo { impl Win32Impl { /// Create a new `Win32Impl` from a `Win32WindowHandle`. - /// + /// /// # Safety - /// + /// /// The `Win32WindowHandle` must be a valid window handle. pub unsafe fn new(handle: &Win32WindowHandle) -> Result { // It is valid for the window handle to be null here. Error out if it is. @@ -56,17 +56,12 @@ impl Win32Impl { )); } - Ok(Self { - dc, - window: hwnd, - }) + Ok(Self { dc, window: hwnd }) } -} -impl GraphicsContextImpl for Win32Impl { - unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { + pub(crate) unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { // Create a new bitmap info struct. - let mut bitmap_info: BitmapInfo =mem::zeroed(); + let mut bitmap_info: BitmapInfo = mem::zeroed(); bitmap_info.bmi_header.biSize = mem::size_of::() as u32; bitmap_info.bmi_header.biPlanes = 1; diff --git a/src/x11.rs b/src/x11.rs index 7a32619..ac51ecf 100644 --- a/src/x11.rs +++ b/src/x11.rs @@ -1,10 +1,10 @@ //! Implementation of software buffering for X11. -//! +//! //! This module converts the input buffer into an XImage and then sends it over the wire to be //! drawn. A more effective implementation would use shared memory instead of the wire. In //! addition, we may also want to blit to a pixmap instead of a window. -use crate::{GraphicsContextImpl, SwBufError}; +use crate::SwBufError; use raw_window_handle::{XlibDisplayHandle, XlibWindowHandle}; use std::os::raw::{c_char, c_uint}; use x11_dl::xlib::{Display, Visual, Xlib, ZPixmap, GC}; @@ -32,9 +32,9 @@ pub struct X11Impl { impl X11Impl { /// Create a new `X11Impl` from a `XlibWindowHandle` and `XlibDisplayHandle`. - /// + /// /// # Safety - /// + /// /// The `XlibWindowHandle` and `XlibDisplayHandle` must be valid. pub unsafe fn new( window_handle: XlibWindowHandle, @@ -83,10 +83,8 @@ impl X11Impl { depth, }) } -} -impl GraphicsContextImpl for X11Impl { - unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { + pub(crate) unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { // Create the image from the buffer. let image = (self.lib.XCreateImage)( self.display_handle.display as *mut Display,