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 ] |
8 | extern crate objc; |
9 | extern crate core; |
10 | |
11 | #[cfg (target_os = "macos" )] |
12 | mod cg; |
13 | #[cfg (kms_platform)] |
14 | mod kms; |
15 | #[cfg (target_os = "redox" )] |
16 | mod orbital; |
17 | #[cfg (wayland_platform)] |
18 | mod wayland; |
19 | #[cfg (target_arch = "wasm32" )] |
20 | mod web; |
21 | #[cfg (target_os = "windows" )] |
22 | mod win32; |
23 | #[cfg (x11_platform)] |
24 | mod x11; |
25 | |
26 | mod error; |
27 | mod util; |
28 | |
29 | use std::marker::PhantomData; |
30 | use std::num::NonZeroU32; |
31 | use std::ops; |
32 | #[cfg (any(wayland_platform, x11_platform, kms_platform))] |
33 | use std::rc::Rc; |
34 | |
35 | pub use error::SoftBufferError; |
36 | |
37 | use raw_window_handle::{ |
38 | HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, |
39 | }; |
40 | |
41 | #[cfg (target_arch = "wasm32" )] |
42 | pub 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. |
46 | pub 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. |
53 | macro_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 | |
175 | make_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 | |
192 | impl 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)] |
252 | pub 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. |
264 | pub 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 | |
270 | impl 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 |
429 | pub struct Buffer<'a> { |
430 | buffer_impl: BufferDispatch<'a>, |
431 | _marker: PhantomData<*mut ()>, |
432 | } |
433 | |
434 | impl<'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 | |
477 | impl<'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 | |
486 | impl<'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 | |
493 | fn 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 | |
512 | fn 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 | |