#![doc = include_str!("../README.md")]
#![deny(unsafe_op_in_unsafe_fn)]
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[cfg(target_os = "macos")]
#[macro_use]
extern crate objc;
extern crate core;
#[cfg(target_os = "macos")]
mod cg;
#[cfg(kms_platform)]
mod kms;
#[cfg(target_os = "redox")]
mod orbital;
#[cfg(wayland_platform)]
mod wayland;
#[cfg(target_arch = "wasm32")]
mod web;
#[cfg(target_os = "windows")]
mod win32;
#[cfg(x11_platform)]
mod x11;
mod error;
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;
pub use error::SoftBufferError;
use raw_window_handle::{
HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
};
#[cfg(target_arch = "wasm32")]
pub use self::web::SurfaceExtWeb;
pub struct Context {
context_impl: ContextDispatch,
_marker: PhantomData<*mut ()>,
}
macro_rules! make_dispatch {
(
$(
$(#[$attr:meta])*
$name: ident ($context_inner: ty, $surface_inner: ty, $buffer_inner: ty),
)*
) => {
enum ContextDispatch {
$(
$(#[$attr])*
$name($context_inner),
)*
}
impl ContextDispatch {
fn variant_name(&self) -> &'static str {
match self {
$(
$(#[$attr])*
Self::$name(_) => stringify!($name),
)*
}
}
}
#[allow(clippy::large_enum_variant)] enum SurfaceDispatch {
$(
$(#[$attr])*
$name($surface_inner),
)*
}
impl SurfaceDispatch {
pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.resize(width, height),
)*
}
}
pub fn buffer_mut(&mut self) -> Result<BufferDispatch, SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => Ok(BufferDispatch::$name(inner.buffer_mut()?)),
)*
}
}
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.fetch(),
)*
}
}
}
enum BufferDispatch<'a> {
$(
$(#[$attr])*
$name($buffer_inner),
)*
}
impl<'a> BufferDispatch<'a> {
#[inline]
pub fn pixels(&self) -> &[u32] {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.pixels(),
)*
}
}
#[inline]
pub fn pixels_mut(&mut self) -> &mut [u32] {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.pixels_mut(),
)*
}
}
pub fn age(&self) -> u8 {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.age(),
)*
}
}
pub fn present(self) -> Result<(), SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.present(),
)*
}
}
pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.present_with_damage(damage),
)*
}
}
}
};
}
make_dispatch! {
#[cfg(x11_platform)]
X11(Rc<x11::X11DisplayImpl>, x11::X11Impl, x11::BufferImpl<'a>),
#[cfg(wayland_platform)]
Wayland(Rc<wayland::WaylandDisplayImpl>, wayland::WaylandImpl, wayland::BufferImpl<'a>),
#[cfg(kms_platform)]
Kms(Rc<kms::KmsDisplayImpl>, kms::KmsImpl, kms::BufferImpl<'a>),
#[cfg(target_os = "windows")]
Win32((), win32::Win32Impl, win32::BufferImpl<'a>),
#[cfg(target_os = "macos")]
CG((), cg::CGImpl, cg::BufferImpl<'a>),
#[cfg(target_arch = "wasm32")]
Web(web::WebDisplayImpl, web::WebImpl, web::BufferImpl<'a>),
#[cfg(target_os = "redox")]
Orbital((), orbital::OrbitalImpl, orbital::BufferImpl<'a>),
}
impl Context {
pub unsafe fn new<D: HasRawDisplayHandle>(display: &D) -> Result<Self, SoftBufferError> {
unsafe { Self::from_raw(display.raw_display_handle()) }
}
pub unsafe fn from_raw(raw_display_handle: RawDisplayHandle) -> Result<Self, SoftBufferError> {
let imple: ContextDispatch = match raw_display_handle {
#[cfg(x11_platform)]
RawDisplayHandle::Xlib(xlib_handle) => unsafe {
ContextDispatch::X11(Rc::new(x11::X11DisplayImpl::from_xlib(xlib_handle)?))
},
#[cfg(x11_platform)]
RawDisplayHandle::Xcb(xcb_handle) => unsafe {
ContextDispatch::X11(Rc::new(x11::X11DisplayImpl::from_xcb(xcb_handle)?))
},
#[cfg(wayland_platform)]
RawDisplayHandle::Wayland(wayland_handle) => unsafe {
ContextDispatch::Wayland(Rc::new(wayland::WaylandDisplayImpl::new(wayland_handle)?))
},
#[cfg(kms_platform)]
RawDisplayHandle::Drm(drm_handle) => unsafe {
ContextDispatch::Kms(Rc::new(kms::KmsDisplayImpl::new(drm_handle)?))
},
#[cfg(target_os = "windows")]
RawDisplayHandle::Windows(_) => ContextDispatch::Win32(()),
#[cfg(target_os = "macos")]
RawDisplayHandle::AppKit(_) => ContextDispatch::CG(()),
#[cfg(target_arch = "wasm32")]
RawDisplayHandle::Web(_) => ContextDispatch::Web(web::WebDisplayImpl::new()?),
#[cfg(target_os = "redox")]
RawDisplayHandle::Orbital(_) => ContextDispatch::Orbital(()),
unimplemented_display_handle => {
return Err(SoftBufferError::UnsupportedDisplayPlatform {
human_readable_display_platform_name: display_handle_type_name(
&unimplemented_display_handle,
),
display_handle: unimplemented_display_handle,
})
}
};
Ok(Self {
context_impl: imple,
_marker: PhantomData,
})
}
}
#[derive(Clone, Copy, Debug)]
pub struct Rect {
pub x: u32,
pub y: u32,
pub width: NonZeroU32,
pub height: NonZeroU32,
}
pub struct Surface {
surface_impl: Box<SurfaceDispatch>,
_marker: PhantomData<*mut ()>,
}
impl Surface {
pub unsafe fn new<W: HasRawWindowHandle>(
context: &Context,
window: &W,
) -> Result<Self, SoftBufferError> {
unsafe { Self::from_raw(context, window.raw_window_handle()) }
}
pub unsafe fn from_raw(
context: &Context,
raw_window_handle: RawWindowHandle,
) -> Result<Self, SoftBufferError> {
let imple: SurfaceDispatch = match (&context.context_impl, raw_window_handle) {
#[cfg(x11_platform)]
(
ContextDispatch::X11(xcb_display_handle),
RawWindowHandle::Xlib(xlib_window_handle),
) => SurfaceDispatch::X11(unsafe {
x11::X11Impl::from_xlib(xlib_window_handle, xcb_display_handle.clone())?
}),
#[cfg(x11_platform)]
(ContextDispatch::X11(xcb_display_handle), RawWindowHandle::Xcb(xcb_window_handle)) => {
SurfaceDispatch::X11(unsafe {
x11::X11Impl::from_xcb(xcb_window_handle, xcb_display_handle.clone())?
})
}
#[cfg(wayland_platform)]
(
ContextDispatch::Wayland(wayland_display_impl),
RawWindowHandle::Wayland(wayland_window_handle),
) => SurfaceDispatch::Wayland(unsafe {
wayland::WaylandImpl::new(wayland_window_handle, wayland_display_impl.clone())?
}),
#[cfg(kms_platform)]
(ContextDispatch::Kms(kms_display_impl), RawWindowHandle::Drm(drm_window_handle)) => {
SurfaceDispatch::Kms(unsafe {
kms::KmsImpl::new(drm_window_handle, kms_display_impl.clone())?
})
}
#[cfg(target_os = "windows")]
(ContextDispatch::Win32(()), RawWindowHandle::Win32(win32_handle)) => {
SurfaceDispatch::Win32(unsafe { win32::Win32Impl::new(&win32_handle)? })
}
#[cfg(target_os = "macos")]
(ContextDispatch::CG(()), RawWindowHandle::AppKit(appkit_handle)) => {
SurfaceDispatch::CG(unsafe { cg::CGImpl::new(appkit_handle)? })
}
#[cfg(target_arch = "wasm32")]
(ContextDispatch::Web(context), RawWindowHandle::Web(web_handle)) => {
SurfaceDispatch::Web(web::WebImpl::new(context, web_handle)?)
}
#[cfg(target_os = "redox")]
(ContextDispatch::Orbital(()), RawWindowHandle::Orbital(orbital_handle)) => {
SurfaceDispatch::Orbital(orbital::OrbitalImpl::new(orbital_handle)?)
}
(unsupported_display_impl, unimplemented_window_handle) => {
return Err(SoftBufferError::UnsupportedWindowPlatform {
human_readable_window_platform_name: window_handle_type_name(
&unimplemented_window_handle,
),
human_readable_display_platform_name: unsupported_display_impl.variant_name(),
window_handle: unimplemented_window_handle,
})
}
};
Ok(Self {
surface_impl: Box::new(imple),
_marker: PhantomData,
})
}
pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
self.surface_impl.resize(width, height)
}
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
self.surface_impl.fetch()
}
pub fn buffer_mut(&mut self) -> Result<Buffer, SoftBufferError> {
Ok(Buffer {
buffer_impl: self.surface_impl.buffer_mut()?,
_marker: PhantomData,
})
}
}
pub struct Buffer<'a> {
buffer_impl: BufferDispatch<'a>,
_marker: PhantomData<*mut ()>,
}
impl<'a> Buffer<'a> {
pub fn age(&self) -> u8 {
self.buffer_impl.age()
}
pub fn present(self) -> Result<(), SoftBufferError> {
self.buffer_impl.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> {
type Target = [u32];
#[inline]
fn deref(&self) -> &[u32] {
self.buffer_impl.pixels()
}
}
impl<'a> ops::DerefMut for Buffer<'a> {
#[inline]
fn deref_mut(&mut self) -> &mut [u32] {
self.buffer_impl.pixels_mut()
}
}
fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str {
match handle {
RawWindowHandle::Xlib(_) => "Xlib",
RawWindowHandle::Win32(_) => "Win32",
RawWindowHandle::WinRt(_) => "WinRt",
RawWindowHandle::Web(_) => "Web",
RawWindowHandle::Wayland(_) => "Wayland",
RawWindowHandle::AndroidNdk(_) => "AndroidNdk",
RawWindowHandle::AppKit(_) => "AppKit",
RawWindowHandle::Orbital(_) => "Orbital",
RawWindowHandle::UiKit(_) => "UiKit",
RawWindowHandle::Xcb(_) => "XCB",
RawWindowHandle::Drm(_) => "DRM",
RawWindowHandle::Gbm(_) => "GBM",
RawWindowHandle::Haiku(_) => "Haiku",
_ => "Unknown Name", }
}
fn display_handle_type_name(handle: &RawDisplayHandle) -> &'static str {
match handle {
RawDisplayHandle::Xlib(_) => "Xlib",
RawDisplayHandle::Web(_) => "Web",
RawDisplayHandle::Wayland(_) => "Wayland",
RawDisplayHandle::AppKit(_) => "AppKit",
RawDisplayHandle::Orbital(_) => "Orbital",
RawDisplayHandle::UiKit(_) => "UiKit",
RawDisplayHandle::Xcb(_) => "XCB",
RawDisplayHandle::Drm(_) => "DRM",
RawDisplayHandle::Gbm(_) => "GBM",
RawDisplayHandle::Haiku(_) => "Haiku",
RawDisplayHandle::Windows(_) => "Windows",
RawDisplayHandle::Android(_) => "Android",
_ => "Unknown Name", }
}