| 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 | |