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