diff --git a/src/backend/winit/mod.rs b/src/backend/winit/mod.rs index 03302cacd01e..02c2f56110f0 100644 --- a/src/backend/winit/mod.rs +++ b/src/backend/winit/mod.rs @@ -19,6 +19,7 @@ //! two traits for the winit backend. use std::io::Error as IoError; +use std::marker::PhantomData; use std::rc::Rc; use std::sync::Arc; use std::time::Duration; @@ -63,7 +64,7 @@ use super::renderer::Renderer; /// Create a new [`WinitGraphicsBackend`], which implements the /// [`Renderer`] trait and a corresponding [`WinitEventLoop`]. -pub fn init() -> Result<(WinitGraphicsBackend, WinitEventLoop), Error> +pub fn init() -> Result, Error> where R: From + Bind>, crate::backend::SwapBuffersError: From<::Error>, @@ -79,9 +80,7 @@ where /// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] /// trait, from a given [`WindowAttributes`] struct and a corresponding /// [`WinitEventLoop`]. -pub fn init_from_attributes( - attributes: WindowAttributes, -) -> Result<(WinitGraphicsBackend, WinitEventLoop), Error> +pub fn init_from_attributes(attributes: WindowAttributes) -> Result, Error> where R: From + Bind>, crate::backend::SwapBuffersError: From<::Error>, @@ -104,7 +103,7 @@ where pub fn init_from_attributes_with_gl_attr( attributes: WindowAttributes, gl_attributes: GlAttributes, -) -> Result<(WinitGraphicsBackend, WinitEventLoop), Error> +) -> Result, Error> where R: From + Bind>, crate::backend::SwapBuffersError: From<::Error>, @@ -115,106 +114,24 @@ where let event_loop = EventLoop::builder().build().map_err(Error::EventLoopCreation)?; - // TODO: Create window in `resumed`? - #[allow(deprecated)] - let window = Arc::new( - event_loop - .create_window(attributes) - .map_err(Error::WindowCreation)?, - ); - - span.record("window", Into::::into(window.id())); - debug!("Window created"); - - let (display, context, surface, is_x11) = { - let display = unsafe { EGLDisplay::new(window.clone())? }; - - let context = - EGLContext::new_with_config(&display, gl_attributes, PixelFormatRequirements::_10_bit()) - .or_else(|_| { - EGLContext::new_with_config(&display, gl_attributes, PixelFormatRequirements::_8_bit()) - })?; - - let (surface, is_x11) = match window.window_handle().map(|handle| handle.as_raw()) { - Ok(RawWindowHandle::Wayland(handle)) => { - debug!("Winit backend: Wayland"); - let size = window.inner_size(); - let surface = unsafe { - wegl::WlEglSurface::new_from_raw( - handle.surface.as_ptr() as *mut _, - size.width as i32, - size.height as i32, - ) - } - .map_err(|err| Error::Surface(err.into()))?; - unsafe { - ( - EGLSurface::new( - &display, - context.pixel_format().unwrap(), - context.config_id(), - surface, - ) - .map_err(EGLError::CreationFailed)?, - false, - ) - } - } - Ok(RawWindowHandle::Xlib(handle)) => { - debug!("Winit backend: X11"); - unsafe { - ( - EGLSurface::new( - &display, - context.pixel_format().unwrap(), - context.config_id(), - native::XlibWindow(handle.window), - ) - .map_err(EGLError::CreationFailed)?, - true, - ) - } - } - _ => panic!("only running on Wayland or with Xlib is supported"), - }; - - let _ = context.unbind(); - (display, context, surface, is_x11) - }; - - let egl = Rc::new(surface); - let renderer = unsafe { GlesRenderer::new(context)?.into() }; - let damage_tracking = display.supports_damage(); - drop(_guard); event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll); let event_loop = Generic::new(event_loop, Interest::READ, calloop::Mode::Level); - Ok(( - WinitGraphicsBackend { - window: window.clone(), - span: span.clone(), - _display: display, - egl_surface: egl, - damage_tracking, - bind_size: None, - renderer, - }, - WinitEventLoop { - inner: WinitEventLoopInner { - scale_factor: window.scale_factor(), - clock: Clock::::new(), - key_counter: 0, - window, - is_x11, - }, - fake_token: None, - event_loop, - pending_events: Vec::new(), + Ok(WinitEventLoop { + inner: WinitEventLoopInner { + clock: Clock::::new(), + key_counter: 0, + window: None, + attributes, + gl_attributes, span, }, - )) + fake_token: None, + event_loop, + pending_events: Vec::new(), + }) } /// Errors thrown by the `winit` backends @@ -359,11 +276,12 @@ where #[derive(Debug)] struct WinitEventLoopInner { - window: Arc, + window: Option>, clock: Clock, key_counter: u32, - is_x11: bool, - scale_factor: f64, + attributes: WindowAttributes, + gl_attributes: GlAttributes, + span: tracing::Span, } /// Abstracted event loop of a [`WinitWindow`]. @@ -372,15 +290,17 @@ struct WinitEventLoopInner { /// [`dispatch_new_events`](WinitEventLoop::dispatch_new_events) periodically to receive any /// events. #[derive(Debug)] -pub struct WinitEventLoop { +pub struct WinitEventLoop { inner: WinitEventLoopInner, fake_token: Option, - pending_events: Vec, + pending_events: Vec>, event_loop: Generic>, - span: tracing::Span, } -impl WinitEventLoop { +impl WinitEventLoop +where + R: From, +{ /// Processes new events of the underlying event loop and calls the provided callback. /// /// You need to periodically call this function to keep the underlying event loop and @@ -392,11 +312,11 @@ impl WinitEventLoop { /// /// The linked [`WinitGraphicsBackend`] will error with a lost context and should /// not be used anymore as well. - #[instrument(level = "trace", parent = &self.span, skip_all)] + #[instrument(level = "trace", parent = &self.inner.span, skip_all)] #[profiling::function] pub fn dispatch_new_events(&mut self, callback: F) -> PumpStatus where - F: FnMut(WinitEvent), + F: FnMut(WinitEvent), { // SAFETY: we don't drop event loop ourselves. let event_loop = unsafe { self.event_loop.get_mut() }; @@ -406,30 +326,141 @@ impl WinitEventLoop { &mut WinitEventLoopApp { inner: &mut self.inner, callback, + _renderer: PhantomData, }, ) } } -struct WinitEventLoopApp<'a, F: FnMut(WinitEvent)> { +struct WinitEventLoopApp<'a, R, F: FnMut(WinitEvent)> { inner: &'a mut WinitEventLoopInner, callback: F, + _renderer: PhantomData, } -impl<'a, F: FnMut(WinitEvent)> WinitEventLoopApp<'a, F> { +impl<'a, R, F: FnMut(WinitEvent)> WinitEventLoopApp<'a, R, F> +where + R: From, +{ fn timestamp(&self) -> u64 { self.inner.clock.now().as_micros() } + + pub fn create_window(&mut self, event_loop: &ActiveEventLoop) -> Result, Error> { + let span = info_span!("backend_winit", window = tracing::field::Empty); + let _guard = span.enter(); + info!("Initializing a winit backend"); + + let window = Arc::new( + event_loop + .create_window(self.inner.attributes.clone()) + .map_err(Error::WindowCreation)?, + ); + + span.record("window", Into::::into(window.id())); + debug!("Window created"); + + let (display, context, surface) = { + let display = unsafe { EGLDisplay::new(window.clone())? }; + + let context = EGLContext::new_with_config( + &display, + self.inner.gl_attributes, + PixelFormatRequirements::_10_bit(), + ) + .or_else(|_| { + EGLContext::new_with_config( + &display, + self.inner.gl_attributes, + PixelFormatRequirements::_8_bit(), + ) + })?; + + let surface = match window.window_handle().map(|handle| handle.as_raw()) { + Ok(RawWindowHandle::Wayland(handle)) => { + debug!("Winit backend: Wayland"); + let size = window.inner_size(); + let surface = unsafe { + wegl::WlEglSurface::new_from_raw( + handle.surface.as_ptr() as *mut _, + size.width as i32, + size.height as i32, + ) + } + .map_err(|err| Error::Surface(err.into()))?; + unsafe { + EGLSurface::new( + &display, + context.pixel_format().unwrap(), + context.config_id(), + surface, + ) + .map_err(EGLError::CreationFailed)? + } + } + Ok(RawWindowHandle::Xlib(handle)) => { + debug!("Winit backend: X11"); + unsafe { + EGLSurface::new( + &display, + context.pixel_format().unwrap(), + context.config_id(), + native::XlibWindow(handle.window), + ) + .map_err(EGLError::CreationFailed)? + } + } + _ => panic!("only running on Wayland or with Xlib is supported"), + }; + + let _ = context.unbind(); + (display, context, surface) + }; + + let egl = Rc::new(surface); + let renderer = unsafe { GlesRenderer::new(context)?.into() }; + let damage_tracking = display.supports_damage(); + + drop(_guard); + + self.inner.window = Some(window.clone()); + + Ok(WinitGraphicsBackend { + window, + span: span.clone(), + _display: display, + egl_surface: egl, + damage_tracking, + bind_size: None, + renderer, + }) + } } -impl<'a, F: FnMut(WinitEvent)> ApplicationHandler for WinitEventLoopApp<'a, F> { - fn resumed(&mut self, _event_loop: &ActiveEventLoop) { +impl<'a, R, F: FnMut(WinitEvent)> ApplicationHandler for WinitEventLoopApp<'a, R, F> +where + R: From, +{ + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = self.create_window(event_loop).unwrap(); + (self.callback)(WinitEvent::WindowCreated(window)); + (self.callback)(WinitEvent::Input(InputEvent::DeviceAdded { device: WinitVirtualDevice, })); } + fn suspended(&mut self, _event_loop: &ActiveEventLoop) { + (self.callback)(WinitEvent::Input(InputEvent::DeviceRemoved { + device: WinitVirtualDevice, + })); + } + fn window_event(&mut self, _event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) { + let Some(window) = self.inner.window.as_ref() else { + return; + }; + match event { WindowEvent::Resized(size) => { trace!("Resizing window to {size:?}"); @@ -437,7 +468,7 @@ impl<'a, F: FnMut(WinitEvent)> ApplicationHandler for WinitEventLoopApp<'a, F> { (self.callback)(WinitEvent::Resized { size: (w, h).into(), - scale_factor: self.inner.scale_factor, + scale_factor: window.scale_factor(), }); } WindowEvent::ScaleFactorChanged { @@ -445,11 +476,10 @@ impl<'a, F: FnMut(WinitEvent)> ApplicationHandler for WinitEventLoopApp<'a, F> { .. } => { trace!("Scale factor changed to {new_scale_factor}"); - self.inner.scale_factor = new_scale_factor; - let (w, h): (i32, i32) = self.inner.window.inner_size().into(); + let (w, h): (i32, i32) = window.inner_size().into(); (self.callback)(WinitEvent::Resized { size: (w, h).into(), - scale_factor: self.inner.scale_factor, + scale_factor: new_scale_factor, }); } WindowEvent::RedrawRequested => { @@ -483,7 +513,7 @@ impl<'a, F: FnMut(WinitEvent)> ApplicationHandler for WinitEventLoopApp<'a, F> { (self.callback)(WinitEvent::Input(event)); } WindowEvent::CursorMoved { position, .. } => { - let size = self.inner.window.inner_size(); + let size = window.inner_size(); let x = position.x / size.width as f64; let y = position.y / size.height as f64; let event = InputEvent::PointerMotionAbsolute { @@ -510,7 +540,7 @@ impl<'a, F: FnMut(WinitEvent)> ApplicationHandler for WinitEventLoopApp<'a, F> { time: self.timestamp(), button, state, - is_x11: self.inner.is_x11, + is_x11: matches!(window.window_handle().unwrap().as_raw(), RawWindowHandle::Xlib(_)), }, }; (self.callback)(WinitEvent::Input(event)); @@ -521,7 +551,7 @@ impl<'a, F: FnMut(WinitEvent)> ApplicationHandler for WinitEventLoopApp<'a, F> { id, .. }) => { - let size = self.inner.window.inner_size(); + let size = window.inner_size(); let x = location.x / size.width as f64; let y = location.y / size.width as f64; let event = InputEvent::TouchDown { @@ -541,7 +571,7 @@ impl<'a, F: FnMut(WinitEvent)> ApplicationHandler for WinitEventLoopApp<'a, F> { id, .. }) => { - let size = self.inner.window.inner_size(); + let size = window.inner_size(); let x = location.x / size.width as f64; let y = location.y / size.width as f64; let event = InputEvent::TouchMotion { @@ -562,7 +592,7 @@ impl<'a, F: FnMut(WinitEvent)> ApplicationHandler for WinitEventLoopApp<'a, F> { id, .. }) => { - let size = self.inner.window.inner_size(); + let size = window.inner_size(); let x = location.x / size.width as f64; let y = location.y / size.width as f64; let event = InputEvent::TouchMotion { @@ -621,8 +651,11 @@ impl<'a, F: FnMut(WinitEvent)> ApplicationHandler for WinitEventLoopApp<'a, F> { } } -impl EventSource for WinitEventLoop { - type Event = WinitEvent; +impl EventSource for WinitEventLoop +where + R: From, +{ + type Event = WinitEvent; type Metadata = (); type Ret = (); type Error = IoError; @@ -689,7 +722,9 @@ impl EventSource for WinitEventLoop { /// Specific events generated by Winit #[derive(Debug)] -pub enum WinitEvent { +pub enum WinitEvent { + WindowCreated(WinitGraphicsBackend), + /// The window has been resized Resized { /// The new physical size (in pixels)