softbuffer/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(unsafe_op_in_unsafe_fn)]
3#![warn(missing_docs)]
4#![cfg_attr(docsrs, feature(doc_auto_cfg))]
5
6#[cfg(target_os = "macos")]
7#[macro_use]
8extern crate objc;
9extern crate core;
10
11#[cfg(target_os = "macos")]
12mod cg;
13#[cfg(kms_platform)]
14mod kms;
15#[cfg(target_os = "redox")]
16mod orbital;
17#[cfg(wayland_platform)]
18mod wayland;
19#[cfg(target_arch = "wasm32")]
20mod web;
21#[cfg(target_os = "windows")]
22mod win32;
23#[cfg(x11_platform)]
24mod x11;
25
26mod error;
27mod util;
28
29use std::marker::PhantomData;
30use std::num::NonZeroU32;
31use std::ops;
32#[cfg(any(wayland_platform, x11_platform, kms_platform))]
33use std::rc::Rc;
34
35use error::InitError;
36pub use error::SoftBufferError;
37
38use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle};
39
40#[cfg(target_arch = "wasm32")]
41pub use self::web::SurfaceExtWeb;
42
43/// An instance of this struct contains the platform-specific data that must be managed in order to
44/// write to a window on that platform.
45pub struct Context<D> {
46    _marker: PhantomData<*mut ()>,
47
48    /// The inner static dispatch object.
49    context_impl: ContextDispatch<D>,
50}
51
52/// A macro for creating the enum used to statically dispatch to the platform-specific implementation.
53macro_rules! make_dispatch {
54    (
55        <$dgen: ident, $wgen: ident> =>
56        $(
57            $(#[$attr:meta])*
58            $name: ident
59            ($context_inner: ty, $surface_inner: ty, $buffer_inner: ty),
60        )*
61    ) => {
62        enum ContextDispatch<$dgen> {
63            $(
64                $(#[$attr])*
65                $name($context_inner),
66            )*
67        }
68
69        impl<D: HasDisplayHandle> ContextDispatch<D> {
70            fn variant_name(&self) -> &'static str {
71                match self {
72                    $(
73                        $(#[$attr])*
74                        Self::$name(_) => stringify!($name),
75                    )*
76                }
77            }
78        }
79
80        #[allow(clippy::large_enum_variant)] // it's boxed anyways
81        enum SurfaceDispatch<$dgen, $wgen> {
82            $(
83                $(#[$attr])*
84                $name($surface_inner),
85            )*
86        }
87
88        impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceDispatch<D, W> {
89            fn window(&self) -> &W {
90                match self {
91                    $(
92                        $(#[$attr])*
93                        Self::$name(inner) => inner.window(),
94                    )*
95                }
96            }
97
98            pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
99                match self {
100                    $(
101                        $(#[$attr])*
102                        Self::$name(inner) => inner.resize(width, height),
103                    )*
104                }
105            }
106
107            pub fn buffer_mut(&mut self) -> Result<BufferDispatch<'_, D, W>, SoftBufferError> {
108                match self {
109                    $(
110                        $(#[$attr])*
111                        Self::$name(inner) => Ok(BufferDispatch::$name(inner.buffer_mut()?)),
112                    )*
113                }
114            }
115
116            pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
117                match self {
118                    $(
119                        $(#[$attr])*
120                        Self::$name(inner) => inner.fetch(),
121                    )*
122                }
123            }
124        }
125
126        enum BufferDispatch<'a, $dgen, $wgen> {
127            $(
128                $(#[$attr])*
129                $name($buffer_inner),
130            )*
131        }
132
133        impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferDispatch<'a, D, W> {
134            #[inline]
135            pub fn pixels(&self) -> &[u32] {
136                match self {
137                    $(
138                        $(#[$attr])*
139                        Self::$name(inner) => inner.pixels(),
140                    )*
141                }
142            }
143
144            #[inline]
145            pub fn pixels_mut(&mut self) -> &mut [u32] {
146                match self {
147                    $(
148                        $(#[$attr])*
149                        Self::$name(inner) => inner.pixels_mut(),
150                    )*
151                }
152            }
153
154            pub fn age(&self) -> u8 {
155                match self {
156                    $(
157                        $(#[$attr])*
158                        Self::$name(inner) => inner.age(),
159                    )*
160                }
161            }
162
163            pub fn present(self) -> Result<(), SoftBufferError> {
164                match self {
165                    $(
166                        $(#[$attr])*
167                        Self::$name(inner) => inner.present(),
168                    )*
169                }
170            }
171
172            pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
173                match self {
174                    $(
175                        $(#[$attr])*
176                        Self::$name(inner) => inner.present_with_damage(damage),
177                    )*
178                }
179            }
180        }
181    };
182}
183
184// XXX empty enum with generic bound is invalid?
185
186make_dispatch! {
187    <D, W> =>
188    #[cfg(x11_platform)]
189    X11(Rc<x11::X11DisplayImpl<D>>, x11::X11Impl<D, W>, x11::BufferImpl<'a, D, W>),
190    #[cfg(wayland_platform)]
191    Wayland(Rc<wayland::WaylandDisplayImpl<D>>, wayland::WaylandImpl<D, W>, wayland::BufferImpl<'a, D, W>),
192    #[cfg(kms_platform)]
193    Kms(Rc<kms::KmsDisplayImpl<D>>, kms::KmsImpl<D, W>, kms::BufferImpl<'a, D, W>),
194    #[cfg(target_os = "windows")]
195    Win32(D, win32::Win32Impl<D, W>, win32::BufferImpl<'a, D, W>),
196    #[cfg(target_os = "macos")]
197    CG(D, cg::CGImpl<D, W>, cg::BufferImpl<'a, D, W>),
198    #[cfg(target_arch = "wasm32")]
199    Web(web::WebDisplayImpl<D>, web::WebImpl<D, W>, web::BufferImpl<'a, D, W>),
200    #[cfg(target_os = "redox")]
201    Orbital(D, orbital::OrbitalImpl<D, W>, orbital::BufferImpl<'a, D, W>),
202}
203
204impl<D: HasDisplayHandle> Context<D> {
205    /// Creates a new instance of this struct, using the provided display.
206    pub fn new(mut dpy: D) -> Result<Self, SoftBufferError> {
207        macro_rules! try_init {
208            ($imp:ident, $x:ident => $make_it:expr) => {{
209                let $x = dpy;
210                match { $make_it } {
211                    Ok(x) => {
212                        return Ok(Self {
213                            context_impl: ContextDispatch::$imp(x),
214                            _marker: PhantomData,
215                        })
216                    }
217                    Err(InitError::Unsupported(d)) => dpy = d,
218                    Err(InitError::Failure(f)) => return Err(f),
219                }
220            }};
221        }
222
223        #[cfg(x11_platform)]
224        try_init!(X11, display => x11::X11DisplayImpl::new(display).map(Rc::new));
225        #[cfg(wayland_platform)]
226        try_init!(Wayland, display => wayland::WaylandDisplayImpl::new(display).map(Rc::new));
227        #[cfg(kms_platform)]
228        try_init!(Kms, display => kms::KmsDisplayImpl::new(display).map(Rc::new));
229        #[cfg(target_os = "windows")]
230        try_init!(Win32, display => Ok(display));
231        #[cfg(target_os = "macos")]
232        try_init!(CG, display => Ok(display));
233        #[cfg(target_arch = "wasm32")]
234        try_init!(Web, display => web::WebDisplayImpl::new(display));
235        #[cfg(target_os = "redox")]
236        try_init!(Orbital, display => Ok(display));
237
238        let raw = dpy.display_handle()?.as_raw();
239        Err(SoftBufferError::UnsupportedDisplayPlatform {
240            human_readable_display_platform_name: display_handle_type_name(&raw),
241            display_handle: raw,
242        })
243    }
244}
245
246/// A rectangular region of the buffer coordinate space.
247#[derive(Clone, Copy, Debug)]
248pub struct Rect {
249    /// x coordinate of top left corner
250    pub x: u32,
251    /// y coordinate of top left corner
252    pub y: u32,
253    /// width
254    pub width: NonZeroU32,
255    /// height
256    pub height: NonZeroU32,
257}
258
259/// A surface for drawing to a window with software buffers.
260pub struct Surface<D, W> {
261    /// This is boxed so that `Surface` is the same size on every platform.
262    surface_impl: Box<SurfaceDispatch<D, W>>,
263    _marker: PhantomData<*mut ()>,
264}
265
266impl<D: HasDisplayHandle, W: HasWindowHandle> Surface<D, W> {
267    /// Creates a new surface for the context for the provided window.
268    pub fn new(context: &Context<D>, window: W) -> Result<Self, SoftBufferError> {
269        macro_rules! leap {
270            ($e:expr) => {{
271                match ($e) {
272                    Ok(x) => x,
273                    Err(InitError::Unsupported(window)) => {
274                        let raw = window.window_handle()?.as_raw();
275                        return Err(SoftBufferError::UnsupportedWindowPlatform {
276                            human_readable_window_platform_name: window_handle_type_name(&raw),
277                            human_readable_display_platform_name: context
278                                .context_impl
279                                .variant_name(),
280                            window_handle: raw,
281                        });
282                    }
283                    Err(InitError::Failure(f)) => return Err(f),
284                }
285            }};
286        }
287
288        let imple = match &context.context_impl {
289            #[cfg(x11_platform)]
290            ContextDispatch::X11(xcb_display_handle) => {
291                SurfaceDispatch::X11(leap!(x11::X11Impl::new(window, xcb_display_handle.clone())))
292            }
293            #[cfg(wayland_platform)]
294            ContextDispatch::Wayland(wayland_display_impl) => SurfaceDispatch::Wayland(leap!(
295                wayland::WaylandImpl::new(window, wayland_display_impl.clone())
296            )),
297            #[cfg(kms_platform)]
298            ContextDispatch::Kms(kms_display_impl) => {
299                SurfaceDispatch::Kms(leap!(kms::KmsImpl::new(window, kms_display_impl.clone())))
300            }
301            #[cfg(target_os = "windows")]
302            ContextDispatch::Win32(_) => {
303                SurfaceDispatch::Win32(leap!(win32::Win32Impl::new(window)))
304            }
305            #[cfg(target_os = "macos")]
306            ContextDispatch::CG(_) => SurfaceDispatch::CG(leap!(cg::CGImpl::new(window))),
307            #[cfg(target_arch = "wasm32")]
308            ContextDispatch::Web(web_display_impl) => {
309                SurfaceDispatch::Web(leap!(web::WebImpl::new(web_display_impl, window)))
310            }
311            #[cfg(target_os = "redox")]
312            ContextDispatch::Orbital(_) => {
313                SurfaceDispatch::Orbital(leap!(orbital::OrbitalImpl::new(window)))
314            }
315        };
316
317        Ok(Self {
318            surface_impl: Box::new(imple),
319            _marker: PhantomData,
320        })
321    }
322
323    /// Get a reference to the underlying window handle.
324    pub fn window(&self) -> &W {
325        self.surface_impl.window()
326    }
327
328    /// Set the size of the buffer that will be returned by [`Surface::buffer_mut`].
329    ///
330    /// If the size of the buffer does not match the size of the window, the buffer is drawn
331    /// in the upper-left corner of the window. It is recommended in most production use cases
332    /// to have the buffer fill the entire window. Use your windowing library to find the size
333    /// of the window.
334    pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
335        self.surface_impl.resize(width, height)
336    }
337
338    /// Copies the window contents into a buffer.
339    ///
340    /// ## Platform Dependent Behavior
341    ///
342    /// - On X11, the window must be visible.
343    /// - On macOS, Redox and Wayland, this function is unimplemented.
344    /// - On Web, this will fail if the content was supplied by
345    ///   a different origin depending on the sites CORS rules.
346    pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
347        self.surface_impl.fetch()
348    }
349
350    /// Return a [`Buffer`] that the next frame should be rendered into. The size must
351    /// be set with [`Surface::resize`] first. The initial contents of the buffer may be zeroed, or
352    /// may contain a previous frame. Call [`Buffer::age`] to determine this.
353    ///
354    /// ## Platform Dependent Behavior
355    ///
356    /// - On DRM/KMS, there is no reliable and sound way to wait for the page flip to happen from within
357    ///   `softbuffer`. Therefore it is the responsibility of the user to wait for the page flip before
358    ///   sending another frame.
359    pub fn buffer_mut(&mut self) -> Result<Buffer<'_, D, W>, SoftBufferError> {
360        Ok(Buffer {
361            buffer_impl: self.surface_impl.buffer_mut()?,
362            _marker: PhantomData,
363        })
364    }
365}
366
367impl<D: HasDisplayHandle, W: HasWindowHandle> AsRef<W> for Surface<D, W> {
368    #[inline]
369    fn as_ref(&self) -> &W {
370        self.window()
371    }
372}
373
374impl<D: HasDisplayHandle, W: HasWindowHandle> HasWindowHandle for Surface<D, W> {
375    #[inline]
376    fn window_handle(
377        &self,
378    ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
379        self.window().window_handle()
380    }
381}
382
383/// A buffer that can be written to by the CPU and presented to the window.
384///
385/// This derefs to a `[u32]`, which depending on the backend may be a mapping into shared memory
386/// accessible to the display server, so presentation doesn't require any (client-side) copying.
387///
388/// This trusts the display server not to mutate the buffer, which could otherwise be unsound.
389///
390/// # Data representation
391///
392/// The format of the buffer is as follows. There is one `u32` in the buffer for each pixel in
393/// the area to draw. The first entry is the upper-left most pixel. The second is one to the right
394/// etc. (Row-major top to bottom left to right one `u32` per pixel). Within each `u32` the highest
395/// order 8 bits are to be set to 0. The next highest order 8 bits are the red channel, then the
396/// green channel, and then the blue channel in the lowest-order 8 bits. See the examples for
397/// one way to build this format using bitwise operations.
398///
399/// --------
400///
401/// Pixel format (`u32`):
402///
403/// 00000000RRRRRRRRGGGGGGGGBBBBBBBB
404///
405/// 0: Bit is 0
406/// R: Red channel
407/// G: Green channel
408/// B: Blue channel
409///
410/// # Platform dependent behavior
411/// No-copy presentation is currently supported on:
412/// - Wayland
413/// - X, when XShm is available
414/// - Win32
415/// - Orbital, when buffer size matches window size
416///
417/// Currently [`Buffer::present`] must block copying image data on:
418/// - Web
419/// - macOS
420pub struct Buffer<'a, D, W> {
421    buffer_impl: BufferDispatch<'a, D, W>,
422    _marker: PhantomData<*mut ()>,
423}
424
425impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W> {
426    /// Is age is the number of frames ago this buffer was last presented. So if the value is
427    /// `1`, it is the same as the last frame, and if it is `2`, it is the same as the frame
428    /// before that (for backends using double buffering). If the value is `0`, it is a new
429    /// buffer that has unspecified contents.
430    ///
431    /// This can be used to update only a portion of the buffer.
432    pub fn age(&self) -> u8 {
433        self.buffer_impl.age()
434    }
435
436    /// Presents buffer to the window.
437    ///
438    /// # Platform dependent behavior
439    ///
440    /// ## Wayland
441    ///
442    /// On Wayland, calling this function may send requests to the underlying `wl_surface`. The
443    /// graphics context may issue `wl_surface.attach`, `wl_surface.damage`, `wl_surface.damage_buffer`
444    /// and `wl_surface.commit` requests when presenting the buffer.
445    ///
446    /// If the caller wishes to synchronize other surface/window changes, such requests must be sent to the
447    /// Wayland compositor before calling this function.
448    pub fn present(self) -> Result<(), SoftBufferError> {
449        self.buffer_impl.present()
450    }
451
452    /// Presents buffer to the window, with damage regions.
453    ///
454    /// # Platform dependent behavior
455    ///
456    /// Supported on:
457    /// - Wayland
458    /// - X, when XShm is available
459    /// - Win32
460    /// - Web
461    ///
462    /// Otherwise this is equivalent to [`Self::present`].
463    pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
464        self.buffer_impl.present_with_damage(damage)
465    }
466}
467
468impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'a, D, W> {
469    type Target = [u32];
470
471    #[inline]
472    fn deref(&self) -> &[u32] {
473        self.buffer_impl.pixels()
474    }
475}
476
477impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::DerefMut for Buffer<'a, D, W> {
478    #[inline]
479    fn deref_mut(&mut self) -> &mut [u32] {
480        self.buffer_impl.pixels_mut()
481    }
482}
483
484/// There is no display handle.
485#[derive(Debug)]
486pub struct NoDisplayHandle(core::convert::Infallible);
487
488impl HasDisplayHandle for NoDisplayHandle {
489    fn display_handle(
490        &self,
491    ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
492        match self.0 {}
493    }
494}
495
496/// There is no window handle.
497#[derive(Debug)]
498pub struct NoWindowHandle(());
499
500impl HasWindowHandle for NoWindowHandle {
501    fn window_handle(
502        &self,
503    ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
504        Err(raw_window_handle::HandleError::NotSupported)
505    }
506}
507
508fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str {
509    match handle {
510        RawWindowHandle::Xlib(_) => "Xlib",
511        RawWindowHandle::Win32(_) => "Win32",
512        RawWindowHandle::WinRt(_) => "WinRt",
513        RawWindowHandle::Web(_) => "Web",
514        RawWindowHandle::Wayland(_) => "Wayland",
515        RawWindowHandle::AndroidNdk(_) => "AndroidNdk",
516        RawWindowHandle::AppKit(_) => "AppKit",
517        RawWindowHandle::Orbital(_) => "Orbital",
518        RawWindowHandle::UiKit(_) => "UiKit",
519        RawWindowHandle::Xcb(_) => "XCB",
520        RawWindowHandle::Drm(_) => "DRM",
521        RawWindowHandle::Gbm(_) => "GBM",
522        RawWindowHandle::Haiku(_) => "Haiku",
523        _ => "Unknown Name", //don't completely fail to compile if there is a new raw window handle type that's added at some point
524    }
525}
526
527fn display_handle_type_name(handle: &RawDisplayHandle) -> &'static str {
528    match handle {
529        RawDisplayHandle::Xlib(_) => "Xlib",
530        RawDisplayHandle::Web(_) => "Web",
531        RawDisplayHandle::Wayland(_) => "Wayland",
532        RawDisplayHandle::AppKit(_) => "AppKit",
533        RawDisplayHandle::Orbital(_) => "Orbital",
534        RawDisplayHandle::UiKit(_) => "UiKit",
535        RawDisplayHandle::Xcb(_) => "XCB",
536        RawDisplayHandle::Drm(_) => "DRM",
537        RawDisplayHandle::Gbm(_) => "GBM",
538        RawDisplayHandle::Haiku(_) => "Haiku",
539        RawDisplayHandle::Windows(_) => "Windows",
540        RawDisplayHandle::Android(_) => "Android",
541        _ => "Unknown Name", //don't completely fail to compile if there is a new raw window handle type that's added at some point
542    }
543}