diff --git a/Cargo.toml b/Cargo.toml index 4479781..bd78868 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ x11 = ["bytemuck", "nix", "x11rb", "x11-dl"] [dependencies] log = "0.4.17" -raw-window-handle = "0.5.0" +raw-window-handle = { version = "0.5.0", features = ["std"] } thiserror = "1.0.30" [target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dependencies] @@ -77,3 +77,6 @@ rayon = "1.5.1" members = [ "run-wasm", ] + +[patch.crates-io] +winit = { git = "https://github.com/bread-graphics/winit.git", branch = "window-handle", version = "0.28.3" } diff --git a/README.md b/README.md index f15cb6e..0194b2a 100644 --- a/README.md +++ b/README.md @@ -58,15 +58,16 @@ To run an example with the web backend: `cargo run-wasm --example winit` Example == ```rust,no_run +use std::rc::Rc; use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; fn main() { let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); - let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); + let window = Rc::new(WindowBuilder::new().build(&event_loop).unwrap()); + let context = softbuffer::Context::new(Rc::clone(&window)).unwrap(); + let mut surface = softbuffer::Surface::new(&context, Rc::clone(&window)).unwrap(); event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; diff --git a/examples/animation.rs b/examples/animation.rs index 7fcc740..5f167f0 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -2,13 +2,14 @@ use instant::Instant; #[cfg(not(target_arch = "wasm32"))] use rayon::prelude::*; use std::f64::consts::PI; +use std::rc::Rc; use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; fn main() { let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); + let window = Rc::new(WindowBuilder::new().build(&event_loop).unwrap()); #[cfg(target_arch = "wasm32")] { @@ -24,8 +25,8 @@ fn main() { .unwrap(); } - let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); + let context = softbuffer::Context::new(window.clone()).unwrap(); + let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); let mut old_size = (0, 0); let mut frames = pre_render_frames(0, 0); diff --git a/examples/fruit.rs b/examples/fruit.rs index b64ae47..01d5ee0 100644 --- a/examples/fruit.rs +++ b/examples/fruit.rs @@ -1,4 +1,5 @@ use image::GenericImageView; +use std::rc::Rc; use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; @@ -18,10 +19,12 @@ fn main() { .collect::>(); let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_inner_size(winit::dpi::PhysicalSize::new(fruit.width(), fruit.height())) - .build(&event_loop) - .unwrap(); + let window = Rc::new( + WindowBuilder::new() + .with_inner_size(winit::dpi::PhysicalSize::new(fruit.width(), fruit.height())) + .build(&event_loop) + .unwrap(), + ); #[cfg(target_arch = "wasm32")] { @@ -37,8 +40,8 @@ fn main() { .unwrap(); } - let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); + let context = softbuffer::Context::new(window.clone()).unwrap(); + let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; diff --git a/examples/rectangle.rs b/examples/rectangle.rs index 17b8a20..b01b5ac 100644 --- a/examples/rectangle.rs +++ b/examples/rectangle.rs @@ -1,3 +1,4 @@ +use std::rc::Rc; use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; @@ -21,10 +22,12 @@ fn redraw(buffer: &mut [u32], width: usize, height: usize, flag: bool) { fn main() { let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_title("Press space to show/hide a rectangle") - .build(&event_loop) - .unwrap(); + let window = Rc::new( + WindowBuilder::new() + .with_title("Press space to show/hide a rectangle") + .build(&event_loop) + .unwrap(), + ); #[cfg(target_arch = "wasm32")] { @@ -40,8 +43,8 @@ fn main() { .unwrap(); } - let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); + let context = softbuffer::Context::new(window.clone()).unwrap(); + let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); let mut buffer = Vec::new(); let mut flag = false; diff --git a/examples/winit.rs b/examples/winit.rs index db421a4..06fd723 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -1,10 +1,11 @@ +use std::rc::Rc; use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; fn main() { let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); + let window = Rc::new(WindowBuilder::new().build(&event_loop).unwrap()); #[cfg(target_arch = "wasm32")] { @@ -20,8 +21,8 @@ fn main() { .unwrap(); } - let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); + let context = softbuffer::Context::new(window.clone()).unwrap(); + let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; diff --git a/examples/winit_wrong_sized_buffer.rs b/examples/winit_wrong_sized_buffer.rs index 120e5bc..6f3fb01 100644 --- a/examples/winit_wrong_sized_buffer.rs +++ b/examples/winit_wrong_sized_buffer.rs @@ -1,3 +1,4 @@ +use std::rc::Rc; use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; @@ -7,7 +8,7 @@ const BUFFER_HEIGHT: usize = 128; fn main() { let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); + let window = Rc::new(WindowBuilder::new().build(&event_loop).unwrap()); #[cfg(target_arch = "wasm32")] { @@ -23,8 +24,8 @@ fn main() { .unwrap(); } - let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); + let context = softbuffer::Context::new(window.clone()).unwrap(); + let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; diff --git a/src/error.rs b/src/error.rs index 01ea102..f9a108e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; +use raw_window_handle::{HandleError, RawDisplayHandle, RawWindowHandle}; use std::error::Error; use thiserror::Error; @@ -27,6 +27,9 @@ pub enum SoftBufferError { #[error("The provided display handle is null.")] IncompleteDisplayHandle, + #[error("A handle error occurred: {0}")] + HandleError(#[from] HandleError), + #[error("Platform error")] PlatformError(Option, Option>), } diff --git a/src/lib.rs b/src/lib.rs index 6e424a3..c29ae28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,20 +21,23 @@ mod x11; mod error; -#[cfg(any(wayland_platform, x11_platform))] use std::sync::Arc; pub use error::SoftBufferError; use raw_window_handle::{ - HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, + ActiveHandle, DisplayHandle, HasDisplayHandle, HasRawDisplayHandle, HasRawWindowHandle, + HasWindowHandle, RawDisplayHandle, RawWindowHandle, WindowHandle, }; /// An instance of this struct contains the platform-specific data that must be managed in order to /// write to a window on that platform. -pub struct Context { +pub struct Context { /// The inner static dispatch object. context_impl: ContextDispatch, + + /// The reference to the event loop object. + display: Arc, } /// A macro for creating the enum used to statically dispatch to the platform-specific implementation. @@ -98,23 +101,13 @@ make_dispatch! { Orbital((), orbital::OrbitalImpl), } -impl Context { +impl Context { /// Creates a new instance of this struct, using the provided display. - /// - /// # Safety - /// - /// - Ensure that the provided object is valid for the lifetime of the Context - pub unsafe fn new(display: &D) -> Result { - unsafe { Self::from_raw(display.raw_display_handle()) } - } - - /// Creates a new instance of this struct, using the provided display handles - /// - /// # Safety - /// - /// - Ensure that the provided handle is valid for the lifetime of the Context - pub unsafe fn from_raw(raw_display_handle: RawDisplayHandle) -> Result { - let imple: ContextDispatch = match raw_display_handle { + pub fn new(display: D) -> Result + where + D: Sized, + { + let imple: ContextDispatch = match display.display_handle()?.raw_display_handle() { #[cfg(x11_platform)] RawDisplayHandle::Xlib(xlib_handle) => unsafe { ContextDispatch::X11(Arc::new(x11::X11DisplayImpl::from_xlib(xlib_handle)?)) @@ -149,89 +142,98 @@ impl Context { Ok(Self { context_impl: imple, + display: Arc::new(display), }) } } -pub struct Surface { - /// This is boxed so that `Surface` is the same size on every platform. - surface_impl: Box, -} - -impl Surface { - /// Creates a new instance of this struct, using the provided window and display. +impl Context> { + /// Creates a new instance of this struct, using the provided display handles /// /// # Safety /// - /// - Ensure that the provided objects are valid to draw a 2D buffer to, and are valid for the - /// lifetime of the Context - pub unsafe fn new( - context: &Context, - window: &W, - ) -> Result { - unsafe { Self::from_raw(context, window.raw_window_handle()) } + /// - Ensure that the provided handle is valid for the lifetime of the Context + pub unsafe fn from_raw(raw_display_handle: RawDisplayHandle) -> Result { + // SAFETY: This is safe because the lifetime of the display handle is static. + unsafe { Self::new(DisplayHandle::borrow_raw(raw_display_handle)) } } +} - /// Creates a new instance of this struct, using the provided raw window and display handles - /// - /// # Safety - /// - /// - Ensure that the provided handles are valid to draw a 2D buffer to, and are valid for the - /// lifetime of the Context - pub unsafe fn from_raw( - context: &Context, - raw_window_handle: RawWindowHandle, - ) -> Result { - let imple: SurfaceDispatch = match (&context.context_impl, raw_window_handle) { - #[cfg(x11_platform)] - ( - ContextDispatch::X11(xcb_display_handle), - RawWindowHandle::Xlib(xlib_window_handle), - ) => SurfaceDispatch::X11(unsafe { - x11::X11Impl::from_xlib(xlib_window_handle, xcb_display_handle.clone())? - }), - #[cfg(x11_platform)] - (ContextDispatch::X11(xcb_display_handle), RawWindowHandle::Xcb(xcb_window_handle)) => { - SurfaceDispatch::X11(unsafe { +pub struct Surface { + /// This is boxed so that `Surface` is the same size on every platform. + surface_impl: Box, + + /// Make sure that the display object is still alive. + _display: Arc, + + /// The reference to the window object. + window: W, +} + +impl Surface { + /// Creates a new instance of this struct, using the provided window and display. + pub fn new(context: &Context, window: W) -> Result + where + W: Sized, + { + let raw_window_handle = window.window_handle()?; + let imple: SurfaceDispatch = + match (&context.context_impl, raw_window_handle.raw_window_handle()) { + #[cfg(x11_platform)] + ( + ContextDispatch::X11(xcb_display_handle), + RawWindowHandle::Xlib(xlib_window_handle), + ) => SurfaceDispatch::X11(unsafe { + x11::X11Impl::from_xlib(xlib_window_handle, xcb_display_handle.clone())? + }), + #[cfg(x11_platform)] + ( + ContextDispatch::X11(xcb_display_handle), + RawWindowHandle::Xcb(xcb_window_handle), + ) => SurfaceDispatch::X11(unsafe { x11::X11Impl::from_xcb(xcb_window_handle, xcb_display_handle.clone())? - }) - } - #[cfg(wayland_platform)] - ( - ContextDispatch::Wayland(wayland_display_impl), - RawWindowHandle::Wayland(wayland_window_handle), - ) => SurfaceDispatch::Wayland(unsafe { - wayland::WaylandImpl::new(wayland_window_handle, wayland_display_impl.clone())? - }), - #[cfg(target_os = "windows")] - (ContextDispatch::Win32(()), RawWindowHandle::Win32(win32_handle)) => { - SurfaceDispatch::Win32(unsafe { win32::Win32Impl::new(&win32_handle)? }) - } - #[cfg(target_os = "macos")] - (ContextDispatch::CG(()), RawWindowHandle::AppKit(appkit_handle)) => { - SurfaceDispatch::CG(unsafe { cg::CGImpl::new(appkit_handle)? }) - } - #[cfg(target_arch = "wasm32")] - (ContextDispatch::Web(context), RawWindowHandle::Web(web_handle)) => { - SurfaceDispatch::Web(web::WebImpl::new(context, web_handle)?) - } - #[cfg(target_os = "redox")] - (ContextDispatch::Orbital(()), RawWindowHandle::Orbital(orbital_handle)) => { - SurfaceDispatch::Orbital(orbital::OrbitalImpl::new(orbital_handle)?) - } - (unsupported_display_impl, unimplemented_window_handle) => { - return Err(SoftBufferError::UnsupportedWindowPlatform { - human_readable_window_platform_name: window_handle_type_name( - &unimplemented_window_handle, - ), - human_readable_display_platform_name: unsupported_display_impl.variant_name(), - window_handle: unimplemented_window_handle, - }) - } - }; + }), + #[cfg(wayland_platform)] + ( + ContextDispatch::Wayland(wayland_display_impl), + RawWindowHandle::Wayland(wayland_window_handle), + ) => SurfaceDispatch::Wayland(unsafe { + wayland::WaylandImpl::new(wayland_window_handle, wayland_display_impl.clone())? + }), + #[cfg(target_os = "windows")] + (ContextDispatch::Win32(()), RawWindowHandle::Win32(win32_handle)) => { + SurfaceDispatch::Win32(unsafe { win32::Win32Impl::new(&win32_handle)? }) + } + #[cfg(target_os = "macos")] + (ContextDispatch::CG(()), RawWindowHandle::AppKit(appkit_handle)) => { + SurfaceDispatch::CG(unsafe { cg::CGImpl::new(appkit_handle)? }) + } + #[cfg(target_arch = "wasm32")] + (ContextDispatch::Web(context), RawWindowHandle::Web(web_handle)) => { + SurfaceDispatch::Web(web::WebImpl::new(context, web_handle)?) + } + #[cfg(target_os = "redox")] + (ContextDispatch::Orbital(()), RawWindowHandle::Orbital(orbital_handle)) => { + SurfaceDispatch::Orbital(orbital::OrbitalImpl::new(orbital_handle)?) + } + (unsupported_display_impl, unimplemented_window_handle) => { + return Err(SoftBufferError::UnsupportedWindowPlatform { + human_readable_window_platform_name: window_handle_type_name( + &unimplemented_window_handle, + ), + human_readable_display_platform_name: unsupported_display_impl + .variant_name(), + window_handle: unimplemented_window_handle, + }) + } + }; + + drop(raw_window_handle); Ok(Self { surface_impl: Box::new(imple), + window, + _display: context.display.clone(), }) } @@ -274,6 +276,10 @@ impl Surface { /// Wayland compositor before calling this function. #[inline] pub fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { + let _guard = self.window.window_handle().unwrap(); + + // SAFETY: All of the below is safe because the window handle is valid for the lifetime of the + // context, and the context is valid for the lifetime of the surface. if (width as usize) * (height as usize) != buffer.len() { panic!("The size of the passed buffer is not the correct size. Its length must be exactly width*height."); } @@ -284,6 +290,27 @@ impl Surface { } } +impl Surface> { + /// Creates a new instance of this struct, using the provided raw window and display handles + /// + /// # Safety + /// + /// - Ensure that the provided handles are valid to draw a 2D buffer to, and are valid for the + /// lifetime of the Context + pub unsafe fn from_raw( + context: &Context, + raw_window_handle: RawWindowHandle, + ) -> Result { + // SAFETY: This is safe because the lifetime of the window handle is static. + unsafe { + Self::new( + context, + WindowHandle::borrow_raw(raw_window_handle, ActiveHandle::new_unchecked()), + ) + } + } +} + fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str { match handle { RawWindowHandle::Xlib(_) => "Xlib",