softbuffer/src/lib.rs

292 lines
11 KiB
Rust
Raw Normal View History

2022-01-16 08:43:00 -06:00
#![doc = include_str!("../README.md")]
2022-12-23 04:19:41 +01:00
#![deny(unsafe_op_in_unsafe_fn)]
2022-01-16 08:43:00 -06:00
2022-01-19 11:59:43 +09:00
#[cfg(target_os = "macos")]
#[macro_use]
extern crate objc;
extern crate core;
2022-01-19 11:59:43 +09:00
#[cfg(target_os = "macos")]
mod cg;
2022-12-22 12:35:18 -08:00
#[cfg(target_os = "redox")]
mod orbital;
#[cfg(all(
feature = "wayland",
any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
)
))]
2022-01-19 21:11:20 -06:00
mod wayland;
#[cfg(target_arch = "wasm32")]
mod web;
2022-12-22 12:35:18 -08:00
#[cfg(target_os = "windows")]
mod win32;
#[cfg(all(
feature = "x11",
any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
)
))]
2022-12-22 12:35:18 -08:00
mod x11;
2022-01-16 08:59:29 -06:00
mod error;
2022-01-19 21:11:20 -06:00
pub use error::SoftBufferError;
2022-01-15 08:17:17 -06:00
2022-12-22 12:35:18 -08:00
use raw_window_handle::{
HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
};
2022-01-15 08:17:17 -06:00
2022-01-16 08:17:53 -06:00
/// 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 {
2022-12-22 10:09:47 -08:00
/// The inner static dispatch object.
2022-12-22 12:35:18 -08:00
///
2022-12-22 10:09:47 -08:00
/// This is boxed so that `GraphicsContext` is the same size on every platform, which should
/// hopefully prevent surprises.
2022-12-22 12:35:18 -08:00
graphics_context_impl: Box<Dispatch>,
2022-12-22 10:09:47 -08:00
}
/// 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])*
2022-12-23 04:19:41 +01:00
Self::$name(inner) => unsafe { inner.set_buffer(buffer, width, height) },
2022-12-22 10:09:47 -08:00
)*
}
}
}
};
}
make_dispatch! {
#[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd")))]
2022-12-22 10:09:47 -08:00
X11(x11::X11Impl),
#[cfg(all(feature = "wayland", any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd")))]
2022-12-22 10:09:47 -08:00
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),
2022-01-15 08:17:17 -06:00
}
impl GraphicsContext {
/// Creates a new instance of this struct, using the provided window and display.
2022-01-16 08:17:53 -06:00
///
/// # Safety
///
/// - Ensure that the provided objects are valid to draw a 2D buffer to, and are valid for the
/// lifetime of the GraphicsContext
2022-12-22 12:35:18 -08:00
pub unsafe fn new<W: HasRawWindowHandle, D: HasRawDisplayHandle>(
window: &W,
display: &D,
) -> Result<Self, SoftBufferError> {
2022-12-23 04:19:41 +01:00
unsafe { Self::from_raw(window.raw_window_handle(), display.raw_display_handle()) }
}
/// Creates a new instance of this struct, using the provided raw window and display handles
///
/// # Safety
///
/// - Ensure that the provided handles are valid to draw a 2D buffer to, and are valid for the
/// lifetime of the GraphicsContext
2022-12-22 12:35:18 -08:00
pub unsafe fn from_raw(
raw_window_handle: RawWindowHandle,
raw_display_handle: RawDisplayHandle,
) -> Result<Self, SoftBufferError> {
2022-12-22 10:09:47 -08:00
let imple: Dispatch = match (raw_window_handle, raw_display_handle) {
#[cfg(all(
feature = "x11",
any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
)
))]
2022-12-22 12:35:18 -08:00
(
RawWindowHandle::Xlib(xlib_window_handle),
RawDisplayHandle::Xlib(xlib_display_handle),
2022-12-23 04:19:41 +01:00
) => Dispatch::X11(unsafe {
x11::X11Impl::from_xlib(xlib_window_handle, xlib_display_handle)?
}),
#[cfg(all(
feature = "x11",
any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
)
))]
(
RawWindowHandle::Xcb(xcb_window_handle),
RawDisplayHandle::Xcb(xcb_display_handle),
) => Dispatch::X11(unsafe {
x11::X11Impl::from_xcb(xcb_window_handle, xcb_display_handle)?
2022-12-23 04:19:41 +01:00
}),
#[cfg(all(
feature = "wayland",
any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
)
))]
2022-12-22 12:35:18 -08:00
(
RawWindowHandle::Wayland(wayland_window_handle),
RawDisplayHandle::Wayland(wayland_display_handle),
2022-12-23 04:19:41 +01:00
) => Dispatch::Wayland(unsafe {
wayland::WaylandImpl::new(wayland_window_handle, wayland_display_handle)?
}),
#[cfg(target_os = "windows")]
2022-12-22 12:35:18 -08:00
(RawWindowHandle::Win32(win32_handle), _) => {
2022-12-23 04:19:41 +01:00
Dispatch::Win32(unsafe { win32::Win32Impl::new(&win32_handle)? })
2022-12-22 12:35:18 -08:00
}
2022-01-19 11:59:43 +09:00
#[cfg(target_os = "macos")]
2022-12-22 12:35:18 -08:00
(RawWindowHandle::AppKit(appkit_handle), _) => {
2022-12-23 04:19:41 +01:00
Dispatch::CG(unsafe { cg::CGImpl::new(appkit_handle)? })
2022-12-22 12:35:18 -08:00
}
#[cfg(target_arch = "wasm32")]
2022-12-22 10:09:47 -08:00
(RawWindowHandle::Web(web_handle), _) => Dispatch::Web(web::WebImpl::new(web_handle)?),
2022-12-07 11:27:36 -07:00
#[cfg(target_os = "redox")]
2022-12-22 12:35:18 -08:00
(RawWindowHandle::Orbital(orbital_handle), _) => {
Dispatch::Orbital(orbital::OrbitalImpl::new(orbital_handle)?)
}
(unimplemented_window_handle, unimplemented_display_handle) => {
return Err(SoftBufferError::UnsupportedPlatform {
2022-12-22 12:35:18 -08:00
human_readable_window_platform_name: window_handle_type_name(
&unimplemented_window_handle,
),
human_readable_display_platform_name: display_handle_type_name(
&unimplemented_display_handle,
),
window_handle: unimplemented_window_handle,
display_handle: unimplemented_display_handle,
})
}
2022-01-15 08:17:17 -06:00
};
2022-01-16 08:59:29 -06:00
Ok(Self {
2022-12-22 12:35:18 -08:00
graphics_context_impl: Box::new(imple),
2022-01-16 08:59:29 -06:00
})
2022-01-15 08:17:17 -06:00
}
2022-01-16 08:17:53 -06:00
/// Shows the given buffer with the given width and height on the window corresponding to this
/// graphics context. Panics if buffer.len() ≠ width*height. If the size of the buffer does
/// not match the size of the window, the buffer is drawn in the upper-left corner of the window.
/// It is recommended in most production use cases to have the buffer fill the entire window.
/// Use your windowing library to find the size of the window.
///
/// The format of the buffer is as follows. There is one u32 in the buffer for each pixel in
/// the area to draw. The first entry is the upper-left most pixel. The second is one to the right
/// etc. (Row-major top to bottom left to right one u32 per pixel). Within each u32 the highest
/// order 8 bits are to be set to 0. The next highest order 8 bits are the red channel, then the
/// green channel, and then the blue channel in the lowest-order 8 bits. See the examples for
/// one way to build this format using bitwise operations.
///
/// --------
///
/// Pixel format (u32):
///
/// 00000000RRRRRRRRGGGGGGGGBBBBBBBB
///
/// 0: Bit is 0
/// R: Red channel
/// G: Green channel
/// B: Blue channel
///
/// # Platform dependent behavior
///
/// This section of the documentation details how some platforms may behave when [`set_buffer`](GraphicsContext::set_buffer)
/// is called.
///
/// ## Wayland
///
/// On Wayland, calling this function may send requests to the underlying `wl_surface`. The
/// graphics context may issue `wl_surface.attach`, `wl_surface.damage`, `wl_surface.damage_buffer`
/// and `wl_surface.commit` requests when presenting the buffer.
///
/// If the caller wishes to synchronize other surface/window changes, such requests must be sent to the
/// Wayland compositor before calling this function.
2022-01-15 08:17:17 -06:00
#[inline]
2022-01-16 08:59:29 -06:00
pub fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
if (width as usize) * (height as usize) != buffer.len() {
panic!("The size of the passed buffer is not the correct size. Its length must be exactly width*height.");
}
2022-01-15 08:17:17 -06:00
unsafe {
self.graphics_context_impl.set_buffer(buffer, width, height);
}
}
}
2022-01-16 08:59:29 -06:00
fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str {
match handle {
2022-01-15 08:17:17 -06:00
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", //don't completely fail to compile if there is a new raw window handle type that's added at some point
}
}
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",
2022-01-16 08:59:29 -06:00
_ => "Unknown Name", //don't completely fail to compile if there is a new raw window handle type that's added at some point
2022-01-15 08:17:17 -06:00
}
2022-01-16 08:59:29 -06:00
}