Skip to content

Commit

Permalink
Merge pull request #196 from rust-windowing/traits
Browse files Browse the repository at this point in the history
Use traits to define common backend interface
  • Loading branch information
ids1024 authored Feb 14, 2024
2 parents 2b44e7a + d7b32fa commit 6a45203
Show file tree
Hide file tree
Showing 14 changed files with 608 additions and 562 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:

- name: Pin versions of dev-deps
if: matrix.rust_version == '1.65.0'
run: cargo update -p half --precise 2.2.1
run: cargo update -p exr --precise 1.71.0 && cargo update -p ahash --precise 0.8.7

- name: Build tests
shell: bash
Expand Down
195 changes: 195 additions & 0 deletions src/backend_dispatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
//! Implements `buffer_interface::*` traits for enums dispatching to backends
use crate::{backend_interface::*, backends, InitError, Rect, SoftBufferError};

use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use std::num::NonZeroU32;
#[cfg(any(wayland_platform, x11_platform, kms_platform))]
use std::rc::Rc;

/// A macro for creating the enum used to statically dispatch to the platform-specific implementation.
macro_rules! make_dispatch {
(
<$dgen: ident, $wgen: ident> =>
$(
$(#[$attr:meta])*
$name: ident
($context_inner: ty, $surface_inner: ty, $buffer_inner: ty),
)*
) => {
pub(crate) enum ContextDispatch<$dgen> {
$(
$(#[$attr])*
$name($context_inner),
)*
}

impl<D: HasDisplayHandle> ContextDispatch<D> {
pub fn variant_name(&self) -> &'static str {
match self {
$(
$(#[$attr])*
Self::$name(_) => stringify!($name),
)*
}
}
}

impl<D: HasDisplayHandle> ContextInterface<D> for ContextDispatch<D> {
fn new(mut display: D) -> Result<Self, InitError<D>>
where
D: Sized,
{
$(
$(#[$attr])*
match <$context_inner as ContextInterface<D>>::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> {
$(
$(#[$attr])*
$name($surface_inner),
)*
}

impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for SurfaceDispatch<D, W> {
type Context = ContextDispatch<D>;
type Buffer<'a> = BufferDispatch<'a, D, W> where Self: 'a;

fn new(window: W, display: &Self::Context) -> Result<Self, InitError<W>>
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 {
$(
$(#[$attr])*
Self::$name(inner) => inner.window(),
)*
}
}

fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.resize(width, height),
)*
}
}

fn buffer_mut(&mut self) -> Result<BufferDispatch<'_, D, W>, SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => Ok(BufferDispatch::$name(inner.buffer_mut()?)),
)*
}
}

fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.fetch(),
)*
}
}
}

pub(crate) enum BufferDispatch<'a, $dgen, $wgen> {
$(
$(#[$attr])*
$name($buffer_inner),
)*
}

impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferDispatch<'a, D, W> {
#[inline]
fn pixels(&self) -> &[u32] {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.pixels(),
)*
}
}

#[inline]
fn pixels_mut(&mut self) -> &mut [u32] {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.pixels_mut(),
)*
}
}

fn age(&self) -> u8 {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.age(),
)*
}
}

fn present(self) -> Result<(), SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.present(),
)*
}
}

fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.present_with_damage(damage),
)*
}
}
}
};
}

// XXX empty enum with generic bound is invalid?

make_dispatch! {
<D, W> =>
#[cfg(x11_platform)]
X11(Rc<backends::x11::X11DisplayImpl<D>>, backends::x11::X11Impl<D, W>, backends::x11::BufferImpl<'a, D, W>),
#[cfg(wayland_platform)]
Wayland(Rc<backends::wayland::WaylandDisplayImpl<D>>, backends::wayland::WaylandImpl<D, W>, backends::wayland::BufferImpl<'a, D, W>),
#[cfg(kms_platform)]
Kms(Rc<backends::kms::KmsDisplayImpl<D>>, backends::kms::KmsImpl<D, W>, backends::kms::BufferImpl<'a, D, W>),
#[cfg(target_os = "windows")]
Win32(D, backends::win32::Win32Impl<D, W>, backends::win32::BufferImpl<'a, D, W>),
#[cfg(target_os = "macos")]
CG(D, backends::cg::CGImpl<D, W>, backends::cg::BufferImpl<'a, D, W>),
#[cfg(target_arch = "wasm32")]
Web(backends::web::WebDisplayImpl<D>, backends::web::WebImpl<D, W>, backends::web::BufferImpl<'a, D, W>),
#[cfg(target_os = "redox")]
Orbital(D, backends::orbital::OrbitalImpl<D, W>, backends::orbital::BufferImpl<'a, D, W>),
}
43 changes: 43 additions & 0 deletions src/backend_interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//! Interface implemented by backends
use crate::{InitError, Rect, SoftBufferError};

use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use std::num::NonZeroU32;

pub(crate) trait ContextInterface<D: HasDisplayHandle + ?Sized> {
fn new(display: D) -> Result<Self, InitError<D>>
where
D: Sized,
Self: Sized;
}

pub(crate) trait SurfaceInterface<D: HasDisplayHandle + ?Sized, W: HasWindowHandle + ?Sized> {
type Context: ContextInterface<D>;
type Buffer<'a>: BufferInterface
where
Self: 'a;

fn new(window: W, context: &Self::Context) -> Result<Self, InitError<W>>
where
W: Sized,
Self: Sized;
/// Get the inner window handle.
fn window(&self) -> &W;
/// Resize the internal buffer to the given width and height.
fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError>;
/// Get a mutable reference to the buffer.
fn buffer_mut(&mut self) -> Result<Self::Buffer<'_>, SoftBufferError>;
/// Fetch the buffer from the window.
fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
}

pub(crate) trait BufferInterface {
fn pixels(&self) -> &[u32];
fn pixels_mut(&mut self) -> &mut [u32];
fn age(&self) -> u8;
fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError>;
fn present(self) -> Result<(), SoftBufferError>;
}
32 changes: 15 additions & 17 deletions src/cg.rs → src/backends/cg.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::backend_interface::*;
use crate::error::InitError;
use crate::{Rect, SoftBufferError};
use core_graphics::base::{
Expand Down Expand Up @@ -34,8 +35,11 @@ pub struct CGImpl<D, W> {
_display: PhantomData<D>,
}

impl<D: HasDisplayHandle, W: HasWindowHandle> CGImpl<D, W> {
pub(crate) fn new(window_src: W) -> Result<Self, InitError<W>> {
impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<D, W> {
type Context = D;
type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a;

fn new(window_src: W, _display: &D) -> Result<Self, InitError<W>> {
let raw = window_src.window_handle()?.as_raw();
let handle = match raw {
RawWindowHandle::AppKit(handle) => handle,
Expand Down Expand Up @@ -66,18 +70,17 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> CGImpl<D, W> {
})
}

/// Get the inner window handle.
#[inline]
pub fn window(&self) -> &W {
fn window(&self) -> &W {
&self.window_handle
}

pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
self.size = Some((width, height));
Ok(())
}

pub fn buffer_mut(&mut self) -> Result<BufferImpl<'_, D, W>, SoftBufferError> {
fn buffer_mut(&mut self) -> Result<BufferImpl<'_, D, W>, SoftBufferError> {
let (width, height) = self
.size
.expect("Must set size of surface before calling `buffer_mut()`");
Expand All @@ -87,34 +90,29 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> CGImpl<D, W> {
imp: self,
})
}

/// Fetch the buffer from the window.
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
Err(SoftBufferError::Unimplemented)
}
}

pub struct BufferImpl<'a, D, W> {
imp: &'a mut CGImpl<D, W>,
buffer: Vec<u32>,
}

impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferImpl<'a, D, W> {
impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W> {
#[inline]
pub fn pixels(&self) -> &[u32] {
fn pixels(&self) -> &[u32] {
&self.buffer
}

#[inline]
pub fn pixels_mut(&mut self) -> &mut [u32] {
fn pixels_mut(&mut self) -> &mut [u32] {
&mut self.buffer
}

pub fn age(&self) -> u8 {
fn age(&self) -> u8 {
0
}

pub fn present(self) -> Result<(), SoftBufferError> {
fn present(self) -> Result<(), SoftBufferError> {
let data_provider = CGDataProvider::from_buffer(Arc::new(Buffer(self.buffer)));
let (width, height) = self.imp.size.unwrap();
let image = CGImage::new(
Expand Down Expand Up @@ -148,7 +146,7 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferImpl<'a, D, W> {
Ok(())
}

pub fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
self.present()
}
}
Expand Down
Loading

0 comments on commit 6a45203

Please sign in to comment.