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 | |
7 | extern crate core; |
8 | |
9 | mod backend_dispatch; |
10 | use backend_dispatch::*; |
11 | mod backend_interface; |
12 | use backend_interface::*; |
13 | mod backends; |
14 | mod error; |
15 | mod util; |
16 | |
17 | use std::cell::Cell; |
18 | use std::marker::PhantomData; |
19 | use std::num::NonZeroU32; |
20 | use std::ops; |
21 | use std::sync::Arc; |
22 | |
23 | use error::InitError; |
24 | pub use error::SoftBufferError; |
25 | |
26 | use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle}; |
27 | |
28 | #[cfg (target_arch = "wasm32" )] |
29 | pub 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. |
33 | pub 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 | |
41 | impl<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)] |
63 | pub 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. |
75 | pub 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 | |
81 | impl<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 | |
145 | impl<D: HasDisplayHandle, W: HasWindowHandle> AsRef<W> for Surface<D, W> { |
146 | #[inline ] |
147 | fn as_ref(&self) -> &W { |
148 | self.window() |
149 | } |
150 | } |
151 | |
152 | impl<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 |
199 | pub struct Buffer<'a, D, W> { |
200 | buffer_impl: BufferDispatch<'a, D, W>, |
201 | _marker: PhantomData<(Arc<D>, Cell<()>)>, |
202 | } |
203 | |
204 | impl<'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 | |
247 | impl<'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 | |
256 | impl<'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)] |
266 | pub struct NoDisplayHandle(core::convert::Infallible); |
267 | |
268 | impl 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)] |
278 | pub struct NoWindowHandle(()); |
279 | |
280 | impl 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 | |
288 | fn 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 | |
307 | fn 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" ))] |
326 | fn __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 | |