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;
|
2022-08-24 00:16:20 -05:00
|
|
|
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;
|
2022-12-27 15:17:42 -08:00
|
|
|
#[cfg(wayland_platform)]
|
2022-01-19 21:11:20 -06:00
|
|
|
mod wayland;
|
2022-02-12 20:24:18 +11:00
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
|
mod web;
|
2022-12-22 12:35:18 -08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
|
mod win32;
|
2022-12-27 15:17:42 -08:00
|
|
|
#[cfg(x11_platform)]
|
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
|
|
|
|
2023-01-06 13:08:17 -08:00
|
|
|
#[cfg(any(wayland_platform, x11_platform))]
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
2022-12-27 12:23:27 -08: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
|
2022-12-21 14:37:30 +01:00
|
|
|
/// write to a window on that platform.
|
2023-01-06 13:08:17 -08:00
|
|
|
pub struct Context {
|
2022-12-22 10:09:47 -08:00
|
|
|
/// The inner static dispatch object.
|
2023-01-06 13:08:17 -08:00
|
|
|
context_impl: ContextDispatch,
|
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])*
|
2023-01-06 13:08:17 -08:00
|
|
|
$name: ident ($context_inner: ty, $surface_inner : ty),
|
2022-12-22 10:09:47 -08:00
|
|
|
)*
|
|
|
|
|
) => {
|
2023-01-06 13:08:17 -08:00
|
|
|
enum ContextDispatch {
|
2022-12-22 10:09:47 -08:00
|
|
|
$(
|
|
|
|
|
$(#[$attr])*
|
2023-01-06 13:08:17 -08:00
|
|
|
$name($context_inner),
|
2022-12-22 10:09:47 -08:00
|
|
|
)*
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 13:08:17 -08:00
|
|
|
impl ContextDispatch {
|
|
|
|
|
fn variant_name(&self) -> &'static str {
|
|
|
|
|
match self {
|
|
|
|
|
$(
|
|
|
|
|
$(#[$attr])*
|
|
|
|
|
Self::$name(_) => stringify!($name),
|
|
|
|
|
)*
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum SurfaceDispatch {
|
|
|
|
|
$(
|
|
|
|
|
$(#[$attr])*
|
|
|
|
|
$name($surface_inner),
|
|
|
|
|
)*
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SurfaceDispatch {
|
2022-12-22 10:09:47 -08:00
|
|
|
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! {
|
2022-12-27 15:17:42 -08:00
|
|
|
#[cfg(x11_platform)]
|
2023-01-06 13:08:17 -08:00
|
|
|
X11(Arc<x11::X11DisplayImpl>, x11::X11Impl),
|
2022-12-27 15:17:42 -08:00
|
|
|
#[cfg(wayland_platform)]
|
2023-01-06 13:08:17 -08:00
|
|
|
Wayland(std::sync::Arc<wayland::WaylandDisplayImpl>, wayland::WaylandImpl),
|
2022-12-22 10:09:47 -08:00
|
|
|
#[cfg(target_os = "windows")]
|
2023-01-06 13:08:17 -08:00
|
|
|
Win32((), win32::Win32Impl),
|
2022-12-22 10:09:47 -08:00
|
|
|
#[cfg(target_os = "macos")]
|
2023-01-06 13:08:17 -08:00
|
|
|
CG((), cg::CGImpl),
|
2022-12-22 10:09:47 -08:00
|
|
|
#[cfg(target_arch = "wasm32")]
|
2023-01-14 05:48:44 +00:00
|
|
|
Web(web::WebDisplayImpl, web::WebImpl),
|
2022-12-22 10:09:47 -08:00
|
|
|
#[cfg(target_os = "redox")]
|
2023-01-06 13:08:17 -08:00
|
|
|
Orbital((), orbital::OrbitalImpl),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Context {
|
|
|
|
|
/// Creates a new instance of this struct, using the provided display.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
|
|
|
|
/// - Ensure that the provided object is valid for the lifetime of the Context
|
|
|
|
|
pub unsafe fn new<D: HasRawDisplayHandle>(display: &D) -> Result<Self, SoftBufferError> {
|
|
|
|
|
unsafe { Self::from_raw(display.raw_display_handle()) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Creates a new instance of this struct, using the provided display handles
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
|
|
|
|
/// - Ensure that the provided handle is valid for the lifetime of the Context
|
|
|
|
|
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(Arc::new(x11::X11DisplayImpl::from_xlib(xlib_handle)?))
|
|
|
|
|
},
|
|
|
|
|
#[cfg(x11_platform)]
|
|
|
|
|
RawDisplayHandle::Xcb(xcb_handle) => unsafe {
|
|
|
|
|
ContextDispatch::X11(Arc::new(x11::X11DisplayImpl::from_xcb(xcb_handle)?))
|
|
|
|
|
},
|
|
|
|
|
#[cfg(wayland_platform)]
|
|
|
|
|
RawDisplayHandle::Wayland(wayland_handle) => unsafe {
|
|
|
|
|
ContextDispatch::Wayland(Arc::new(wayland::WaylandDisplayImpl::new(
|
|
|
|
|
wayland_handle,
|
|
|
|
|
)?))
|
|
|
|
|
},
|
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
|
RawDisplayHandle::Windows(_) => ContextDispatch::Win32(()),
|
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
|
RawDisplayHandle::AppKit(_) => ContextDispatch::CG(()),
|
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
2023-01-14 05:48:44 +00:00
|
|
|
RawDisplayHandle::Web(_) => ContextDispatch::Web(web::WebDisplayImpl::new()?),
|
2023-01-06 13:08:17 -08:00
|
|
|
#[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,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Surface {
|
|
|
|
|
/// This is boxed so that `Surface` is the same size on every platform.
|
|
|
|
|
surface_impl: Box<SurfaceDispatch>,
|
2022-01-15 08:17:17 -06:00
|
|
|
}
|
|
|
|
|
|
2023-01-06 13:08:17 -08:00
|
|
|
impl Surface {
|
2022-12-20 10:10:52 -07:00
|
|
|
/// Creates a new instance of this struct, using the provided window and display.
|
2022-01-16 08:17:53 -06:00
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
2022-12-20 10:10:52 -07:00
|
|
|
/// - Ensure that the provided objects are valid to draw a 2D buffer to, and are valid for the
|
2023-01-06 13:08:17 -08:00
|
|
|
/// lifetime of the Context
|
|
|
|
|
pub unsafe fn new<W: HasRawWindowHandle>(
|
|
|
|
|
context: &Context,
|
2022-12-22 12:35:18 -08:00
|
|
|
window: &W,
|
2022-12-27 12:23:27 -08:00
|
|
|
) -> Result<Self, SoftBufferError> {
|
2023-01-06 13:08:17 -08:00
|
|
|
unsafe { Self::from_raw(context, window.raw_window_handle()) }
|
2022-12-20 09:40:06 -07:00
|
|
|
}
|
2022-08-24 00:16:20 -05:00
|
|
|
|
2022-12-20 10:10:52 -07:00
|
|
|
/// Creates a new instance of this struct, using the provided raw window and display handles
|
2022-12-20 09:40:06 -07:00
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
///
|
|
|
|
|
/// - Ensure that the provided handles are valid to draw a 2D buffer to, and are valid for the
|
2023-01-06 13:08:17 -08:00
|
|
|
/// lifetime of the Context
|
2022-12-22 12:35:18 -08:00
|
|
|
pub unsafe fn from_raw(
|
2023-01-06 13:08:17 -08:00
|
|
|
context: &Context,
|
2022-12-22 12:35:18 -08:00
|
|
|
raw_window_handle: RawWindowHandle,
|
2022-12-27 12:23:27 -08:00
|
|
|
) -> Result<Self, SoftBufferError> {
|
2023-01-06 13:08:17 -08:00
|
|
|
let imple: SurfaceDispatch = match (&context.context_impl, raw_window_handle) {
|
2022-12-27 15:17:42 -08:00
|
|
|
#[cfg(x11_platform)]
|
2022-12-22 12:35:18 -08:00
|
|
|
(
|
2023-01-06 13:08:17 -08:00
|
|
|
ContextDispatch::X11(xcb_display_handle),
|
2022-12-22 12:35:18 -08:00
|
|
|
RawWindowHandle::Xlib(xlib_window_handle),
|
2023-01-06 13:08:17 -08:00
|
|
|
) => SurfaceDispatch::X11(unsafe {
|
|
|
|
|
x11::X11Impl::from_xlib(xlib_window_handle, xcb_display_handle.clone())?
|
2022-12-27 09:14:54 -08:00
|
|
|
}),
|
2022-12-27 15:17:42 -08:00
|
|
|
#[cfg(x11_platform)]
|
2023-01-06 13:08:17 -08:00
|
|
|
(ContextDispatch::X11(xcb_display_handle), RawWindowHandle::Xcb(xcb_window_handle)) => {
|
|
|
|
|
SurfaceDispatch::X11(unsafe {
|
|
|
|
|
x11::X11Impl::from_xcb(xcb_window_handle, xcb_display_handle.clone())?
|
|
|
|
|
})
|
|
|
|
|
}
|
2022-12-27 15:17:42 -08:00
|
|
|
#[cfg(wayland_platform)]
|
2022-12-22 12:35:18 -08:00
|
|
|
(
|
2023-01-06 13:08:17 -08:00
|
|
|
ContextDispatch::Wayland(wayland_display_impl),
|
2022-12-22 12:35:18 -08:00
|
|
|
RawWindowHandle::Wayland(wayland_window_handle),
|
2023-01-06 13:08:17 -08:00
|
|
|
) => SurfaceDispatch::Wayland(unsafe {
|
|
|
|
|
wayland::WaylandImpl::new(wayland_window_handle, wayland_display_impl.clone())?
|
2022-12-23 04:19:41 +01:00
|
|
|
}),
|
2022-01-16 08:03:20 -06:00
|
|
|
#[cfg(target_os = "windows")]
|
2023-01-06 13:08:17 -08:00
|
|
|
(ContextDispatch::Win32(()), RawWindowHandle::Win32(win32_handle)) => {
|
|
|
|
|
SurfaceDispatch::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")]
|
2023-01-06 13:08:17 -08:00
|
|
|
(ContextDispatch::CG(()), RawWindowHandle::AppKit(appkit_handle)) => {
|
|
|
|
|
SurfaceDispatch::CG(unsafe { cg::CGImpl::new(appkit_handle)? })
|
2022-12-22 12:35:18 -08:00
|
|
|
}
|
2022-02-12 20:24:18 +11:00
|
|
|
#[cfg(target_arch = "wasm32")]
|
2023-01-14 05:48:44 +00:00
|
|
|
(ContextDispatch::Web(context), RawWindowHandle::Web(web_handle)) => {
|
|
|
|
|
SurfaceDispatch::Web(web::WebImpl::new(context, web_handle)?)
|
2023-01-06 13:08:17 -08:00
|
|
|
}
|
2022-12-07 11:27:36 -07:00
|
|
|
#[cfg(target_os = "redox")]
|
2023-01-06 13:08:17 -08:00
|
|
|
(ContextDispatch::Orbital(()), RawWindowHandle::Orbital(orbital_handle)) => {
|
|
|
|
|
SurfaceDispatch::Orbital(orbital::OrbitalImpl::new(orbital_handle)?)
|
2022-12-22 12:35:18 -08:00
|
|
|
}
|
2023-01-06 13:08:17 -08:00
|
|
|
(unsupported_display_impl, unimplemented_window_handle) => {
|
|
|
|
|
return Err(SoftBufferError::UnsupportedWindowPlatform {
|
2022-12-22 12:35:18 -08:00
|
|
|
human_readable_window_platform_name: window_handle_type_name(
|
|
|
|
|
&unimplemented_window_handle,
|
|
|
|
|
),
|
2023-01-06 13:08:17 -08:00
|
|
|
human_readable_display_platform_name: unsupported_display_impl.variant_name(),
|
2022-12-22 12:35:18 -08:00
|
|
|
window_handle: unimplemented_window_handle,
|
|
|
|
|
})
|
|
|
|
|
}
|
2022-01-15 08:17:17 -06:00
|
|
|
};
|
|
|
|
|
|
2022-01-16 08:59:29 -06:00
|
|
|
Ok(Self {
|
2023-01-06 13:08:17 -08:00
|
|
|
surface_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
|
2023-01-01 13:51:45 -06:00
|
|
|
///
|
|
|
|
|
/// # Platform dependent behavior
|
|
|
|
|
///
|
2023-01-06 13:08:17 -08:00
|
|
|
/// This section of the documentation details how some platforms may behave when [`set_buffer`](Surface::set_buffer)
|
2023-01-01 13:51:45 -06:00
|
|
|
/// 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() {
|
2022-01-16 08:07:39 -06:00
|
|
|
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 {
|
2023-01-06 13:08:17 -08:00
|
|
|
self.surface_impl.set_buffer(buffer, width, height);
|
2022-01-15 08:17:17 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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",
|
2022-08-24 00:16:20 -05:00
|
|
|
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
|
|
|
}
|