1#![doc = include_str!("../README.md")]
2#![allow(clippy::needless_doctest_main)]
3#![deny(unsafe_op_in_unsafe_fn)]
4#![warn(missing_docs)]
5#![cfg_attr(docsrs, feature(doc_auto_cfg))]
6
7extern crate core;
8
9mod backend_dispatch;
10use backend_dispatch::*;
11mod backend_interface;
12use backend_interface::*;
13mod backends;
14mod error;
15mod util;
16
17use std::cell::Cell;
18use std::marker::PhantomData;
19use std::num::NonZeroU32;
20use std::ops;
21use std::sync::Arc;
22
23use error::InitError;
24pub use error::SoftBufferError;
25
26use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle};
27
28#[cfg(target_arch = "wasm32")]
29pub use backends::web::SurfaceExtWeb;
30
31/// An instance of this struct contains the platform-specific data that must be managed in order to
32/// write to a window on that platform.
33pub struct Context<D> {
34 /// The inner static dispatch object.
35 context_impl: ContextDispatch<D>,
36
37 /// This is Send+Sync IFF D is Send+Sync.
38 _marker: PhantomData<Arc<D>>,
39}
40
41impl<D: HasDisplayHandle> Context<D> {
42 /// Creates a new instance of this struct, using the provided display.
43 pub fn new(display: D) -> Result<Self, SoftBufferError> {
44 match ContextDispatch::new(display) {
45 Ok(context_impl: ContextDispatch) => Ok(Self {
46 context_impl,
47 _marker: PhantomData,
48 }),
49 Err(InitError::Unsupported(display: D)) => {
50 let raw: RawDisplayHandle = display.display_handle()?.as_raw();
51 Err(SoftBufferError::UnsupportedDisplayPlatform {
52 human_readable_display_platform_name: display_handle_type_name(&raw),
53 display_handle: raw,
54 })
55 }
56 Err(InitError::Failure(f: SoftBufferError)) => Err(f),
57 }
58 }
59}
60
61/// A rectangular region of the buffer coordinate space.
62#[derive(Clone, Copy, Debug)]
63pub struct Rect {
64 /// x coordinate of top left corner
65 pub x: u32,
66 /// y coordinate of top left corner
67 pub y: u32,
68 /// width
69 pub width: NonZeroU32,
70 /// height
71 pub height: NonZeroU32,
72}
73
74/// A surface for drawing to a window with software buffers.
75pub struct Surface<D, W> {
76 /// This is boxed so that `Surface` is the same size on every platform.
77 surface_impl: Box<SurfaceDispatch<D, W>>,
78 _marker: PhantomData<Cell<()>>,
79}
80
81impl<D: HasDisplayHandle, W: HasWindowHandle> Surface<D, W> {
82 /// Creates a new surface for the context for the provided window.
83 pub fn new(context: &Context<D>, window: W) -> Result<Self, SoftBufferError> {
84 match SurfaceDispatch::new(window, &context.context_impl) {
85 Ok(surface_dispatch) => Ok(Self {
86 surface_impl: Box::new(surface_dispatch),
87 _marker: PhantomData,
88 }),
89 Err(InitError::Unsupported(window)) => {
90 let raw = window.window_handle()?.as_raw();
91 Err(SoftBufferError::UnsupportedWindowPlatform {
92 human_readable_window_platform_name: window_handle_type_name(&raw),
93 human_readable_display_platform_name: context.context_impl.variant_name(),
94 window_handle: raw,
95 })
96 }
97 Err(InitError::Failure(f)) => Err(f),
98 }
99 }
100
101 /// Get a reference to the underlying window handle.
102 pub fn window(&self) -> &W {
103 self.surface_impl.window()
104 }
105
106 /// Set the size of the buffer that will be returned by [`Surface::buffer_mut`].
107 ///
108 /// If the size of the buffer does not match the size of the window, the buffer is drawn
109 /// in the upper-left corner of the window. It is recommended in most production use cases
110 /// to have the buffer fill the entire window. Use your windowing library to find the size
111 /// of the window.
112 pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
113 self.surface_impl.resize(width, height)
114 }
115
116 /// Copies the window contents into a buffer.
117 ///
118 /// ## Platform Dependent Behavior
119 ///
120 /// - On X11, the window must be visible.
121 /// - On AppKit, UIKit, Redox and Wayland, this function is unimplemented.
122 /// - On Web, this will fail if the content was supplied by
123 /// a different origin depending on the sites CORS rules.
124 pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
125 self.surface_impl.fetch()
126 }
127
128 /// Return a [`Buffer`] that the next frame should be rendered into. The size must
129 /// be set with [`Surface::resize`] first. The initial contents of the buffer may be zeroed, or
130 /// may contain a previous frame. Call [`Buffer::age`] to determine this.
131 ///
132 /// ## Platform Dependent Behavior
133 ///
134 /// - On DRM/KMS, there is no reliable and sound way to wait for the page flip to happen from within
135 /// `softbuffer`. Therefore it is the responsibility of the user to wait for the page flip before
136 /// sending another frame.
137 pub fn buffer_mut(&mut self) -> Result<Buffer<'_, D, W>, SoftBufferError> {
138 Ok(Buffer {
139 buffer_impl: self.surface_impl.buffer_mut()?,
140 _marker: PhantomData,
141 })
142 }
143}
144
145impl<D: HasDisplayHandle, W: HasWindowHandle> AsRef<W> for Surface<D, W> {
146 #[inline]
147 fn as_ref(&self) -> &W {
148 self.window()
149 }
150}
151
152impl<D: HasDisplayHandle, W: HasWindowHandle> HasWindowHandle for Surface<D, W> {
153 #[inline]
154 fn window_handle(
155 &self,
156 ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
157 self.window().window_handle()
158 }
159}
160
161/// A buffer that can be written to by the CPU and presented to the window.
162///
163/// This derefs to a `[u32]`, which depending on the backend may be a mapping into shared memory
164/// accessible to the display server, so presentation doesn't require any (client-side) copying.
165///
166/// This trusts the display server not to mutate the buffer, which could otherwise be unsound.
167///
168/// # Data representation
169///
170/// The format of the buffer is as follows. There is one `u32` in the buffer for each pixel in
171/// the area to draw. The first entry is the upper-left most pixel. The second is one to the right
172/// etc. (Row-major top to bottom left to right one `u32` per pixel). Within each `u32` the highest
173/// order 8 bits are to be set to 0. The next highest order 8 bits are the red channel, then the
174/// green channel, and then the blue channel in the lowest-order 8 bits. See the examples for
175/// one way to build this format using bitwise operations.
176///
177/// --------
178///
179/// Pixel format (`u32`):
180///
181/// 00000000RRRRRRRRGGGGGGGGBBBBBBBB
182///
183/// 0: Bit is 0
184/// R: Red channel
185/// G: Green channel
186/// B: Blue channel
187///
188/// # Platform dependent behavior
189/// No-copy presentation is currently supported on:
190/// - Wayland
191/// - X, when XShm is available
192/// - Win32
193/// - Orbital, when buffer size matches window size
194///
195/// Currently [`Buffer::present`] must block copying image data on:
196/// - Web
197/// - AppKit
198/// - UIKit
199pub struct Buffer<'a, D, W> {
200 buffer_impl: BufferDispatch<'a, D, W>,
201 _marker: PhantomData<(Arc<D>, Cell<()>)>,
202}
203
204impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W> {
205 /// Is age is the number of frames ago this buffer was last presented. So if the value is
206 /// `1`, it is the same as the last frame, and if it is `2`, it is the same as the frame
207 /// before that (for backends using double buffering). If the value is `0`, it is a new
208 /// buffer that has unspecified contents.
209 ///
210 /// This can be used to update only a portion of the buffer.
211 pub fn age(&self) -> u8 {
212 self.buffer_impl.age()
213 }
214
215 /// Presents buffer to the window.
216 ///
217 /// # Platform dependent behavior
218 ///
219 /// ## Wayland
220 ///
221 /// On Wayland, calling this function may send requests to the underlying `wl_surface`. The
222 /// graphics context may issue `wl_surface.attach`, `wl_surface.damage`, `wl_surface.damage_buffer`
223 /// and `wl_surface.commit` requests when presenting the buffer.
224 ///
225 /// If the caller wishes to synchronize other surface/window changes, such requests must be sent to the
226 /// Wayland compositor before calling this function.
227 pub fn present(self) -> Result<(), SoftBufferError> {
228 self.buffer_impl.present()
229 }
230
231 /// Presents buffer to the window, with damage regions.
232 ///
233 /// # Platform dependent behavior
234 ///
235 /// Supported on:
236 /// - Wayland
237 /// - X, when XShm is available
238 /// - Win32
239 /// - Web
240 ///
241 /// Otherwise this is equivalent to [`Self::present`].
242 pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
243 self.buffer_impl.present_with_damage(damage)
244 }
245}
246
247impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'a, D, W> {
248 type Target = [u32];
249
250 #[inline]
251 fn deref(&self) -> &[u32] {
252 self.buffer_impl.pixels()
253 }
254}
255
256impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::DerefMut for Buffer<'a, D, W> {
257 #[inline]
258 fn deref_mut(&mut self) -> &mut [u32] {
259 self.buffer_impl.pixels_mut()
260 }
261}
262
263/// There is no display handle.
264#[derive(Debug)]
265#[allow(dead_code)]
266pub struct NoDisplayHandle(core::convert::Infallible);
267
268impl HasDisplayHandle for NoDisplayHandle {
269 fn display_handle(
270 &self,
271 ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
272 match self.0 {}
273 }
274}
275
276/// There is no window handle.
277#[derive(Debug)]
278pub struct NoWindowHandle(());
279
280impl HasWindowHandle for NoWindowHandle {
281 fn window_handle(
282 &self,
283 ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
284 Err(raw_window_handle::HandleError::NotSupported)
285 }
286}
287
288fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str {
289 match handle {
290 RawWindowHandle::Xlib(_) => "Xlib",
291 RawWindowHandle::Win32(_) => "Win32",
292 RawWindowHandle::WinRt(_) => "WinRt",
293 RawWindowHandle::Web(_) => "Web",
294 RawWindowHandle::Wayland(_) => "Wayland",
295 RawWindowHandle::AndroidNdk(_) => "AndroidNdk",
296 RawWindowHandle::AppKit(_) => "AppKit",
297 RawWindowHandle::Orbital(_) => "Orbital",
298 RawWindowHandle::UiKit(_) => "UiKit",
299 RawWindowHandle::Xcb(_) => "XCB",
300 RawWindowHandle::Drm(_) => "DRM",
301 RawWindowHandle::Gbm(_) => "GBM",
302 RawWindowHandle::Haiku(_) => "Haiku",
303 _ => "Unknown Name", //don't completely fail to compile if there is a new raw window handle type that's added at some point
304 }
305}
306
307fn display_handle_type_name(handle: &RawDisplayHandle) -> &'static str {
308 match handle {
309 RawDisplayHandle::Xlib(_) => "Xlib",
310 RawDisplayHandle::Web(_) => "Web",
311 RawDisplayHandle::Wayland(_) => "Wayland",
312 RawDisplayHandle::AppKit(_) => "AppKit",
313 RawDisplayHandle::Orbital(_) => "Orbital",
314 RawDisplayHandle::UiKit(_) => "UiKit",
315 RawDisplayHandle::Xcb(_) => "XCB",
316 RawDisplayHandle::Drm(_) => "DRM",
317 RawDisplayHandle::Gbm(_) => "GBM",
318 RawDisplayHandle::Haiku(_) => "Haiku",
319 RawDisplayHandle::Windows(_) => "Windows",
320 RawDisplayHandle::Android(_) => "Android",
321 _ => "Unknown Name", //don't completely fail to compile if there is a new raw window handle type that's added at some point
322 }
323}
324
325#[cfg(not(target_family = "wasm"))]
326fn __assert_send() {
327 fn is_send<T: Send>() {}
328 fn is_sync<T: Sync>() {}
329
330 is_send::<Context<()>>();
331 is_sync::<Context<()>>();
332 is_send::<Surface<(), ()>>();
333 is_send::<Buffer<'static, (), ()>>();
334
335 /// ```compile_fail
336 /// use softbuffer::Surface;
337 ///
338 /// fn __is_sync<T: Sync>() {}
339 /// __is_sync::<Surface<(), ()>>();
340 /// ```
341 fn __surface_not_sync() {}
342 /// ```compile_fail
343 /// use softbuffer::Buffer;
344 ///
345 /// fn __is_sync<T: Sync>() {}
346 /// __is_sync::<Buffer<'static, (), ()>>();
347 /// ```
348 fn __buffer_not_sync() {}
349}
350