diff options
Diffstat (limited to '')
| -rw-r--r-- | glutin/src/multi_window.rs | 372 | ||||
| -rw-r--r-- | glutin/src/multi_window/state.rs | 20 | 
2 files changed, 294 insertions, 98 deletions
| diff --git a/glutin/src/multi_window.rs b/glutin/src/multi_window.rs index 2a66a816..095e0e2c 100644 --- a/glutin/src/multi_window.rs +++ b/glutin/src/multi_window.rs @@ -15,11 +15,30 @@ use iced_winit::renderer;  use iced_winit::settings;  use iced_winit::user_interface;  use iced_winit::window; +use iced_winit::winit;  use iced_winit::{Clipboard, Command, Debug, Proxy, Settings}; -use glutin::window::Window; +use glutin::config::{ +    Config, ConfigSurfaceTypes, ConfigTemplateBuilder, GlConfig, +}; +use glutin::context::{ +    ContextApi, ContextAttributesBuilder, NotCurrentContext, +    NotCurrentGlContextSurfaceAccessor, +    PossiblyCurrentContextGlSurfaceAccessor, PossiblyCurrentGlContext, +}; +use glutin::display::{Display, DisplayApiPreference, GlDisplay}; +use glutin::surface::{ +    GlSurface, Surface, SurfaceAttributesBuilder, WindowSurface, +}; +use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; +  use std::collections::HashMap; +use std::ffi::CString;  use std::mem::ManuallyDrop; +use std::num::NonZeroU32; + +#[allow(unsafe_code)] +const ONE: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(1) };  /// Runs an [`Application`] with an executor, compositor, and the provided  /// settings. @@ -35,9 +54,8 @@ where  {      use futures::task;      use futures::Future; -    use glutin::event_loop::EventLoopBuilder; -    use glutin::platform::run_return::EventLoopExtRunReturn; -    use glutin::ContextBuilder; +    use winit::event_loop::EventLoopBuilder; +    use winit::platform::run_return::EventLoopExtRunReturn;      let mut debug = Debug::new();      debug.startup_started(); @@ -58,81 +76,216 @@ where          runtime.enter(|| A::new(flags))      }; -    let (context, window) = { -        let builder = settings.window.into_builder( -            &application.title(), -            event_loop.primary_monitor(), -            settings.id, -        ); +    let builder = settings.window.into_builder( +        &application.title(), +        event_loop.primary_monitor(), +        settings.id, +    ); + +    log::info!("Window builder: {:#?}", builder); + +    #[allow(unsafe_code)] +    let (display, window, configuration, surface, context) = unsafe { +        struct Configuration(Config); +        use std::fmt; +        impl fmt::Debug for Configuration { +            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +                let config = &self.0; + +                f.debug_struct("Configuration") +                    .field("raw", &config) +                    .field("samples", &config.num_samples()) +                    .field("buffer_type", &config.color_buffer_type()) +                    .field("surface_type", &config.config_surface_types()) +                    .field("depth", &config.depth_size()) +                    .field("alpha", &config.alpha_size()) +                    .field("stencil", &config.stencil_size()) +                    .field("float_pixels", &config.float_pixels()) +                    .field("srgb", &config.srgb_capable()) +                    .field("api", &config.api()) +                    .finish() +            } +        } -        log::info!("Window builder: {:#?}", builder); +        impl AsRef<Config> for Configuration { +            fn as_ref(&self) -> &Config { +                &self.0 +            } +        } -        let opengl_builder = ContextBuilder::new() -            .with_vsync(true) -            .with_multisampling(C::sample_count(&compositor_settings) as u16); +        let display_handle = event_loop.raw_display_handle(); -        let opengles_builder = opengl_builder.clone().with_gl( -            glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (2, 0)), -        ); +        #[cfg(all( +            any(windows, target_os = "macos"), +            not(target_arch = "wasm32") +        ))] +        let (window, window_handle) = { +            let window = builder +                .build(&event_loop) +                .map_err(Error::WindowCreationFailed)?; + +            let handle = window.raw_window_handle(); + +            (window, handle) +        }; + +        #[cfg(target_arch = "wasm32")] +        let preference = Err(Error::GraphicsCreationFailed( +            iced_graphics::Error::BackendError(format!( +                "target not supported by backend" +            )), +        ))?; + +        #[cfg(all(windows, not(target_arch = "wasm32")))] +        let preference = DisplayApiPreference::WglThenEgl(Some(window_handle)); + +        #[cfg(all(target_os = "macos", not(target_arch = "wasm32")))] +        let preference = DisplayApiPreference::Cgl; + +        #[cfg(all( +            unix, +            not(target_os = "macos"), +            not(target_arch = "wasm32") +        ))] +        let preference = DisplayApiPreference::GlxThenEgl(Box::new( +            winit::platform::unix::register_xlib_error_hook, +        )); + +        let display = +            Display::new(display_handle, preference).map_err(|error| { +                Error::GraphicsCreationFailed( +                    iced_graphics::Error::BackendError(format!( +                        "failed to create display: {error}" +                    )), +                ) +            })?; + +        log::debug!("Display: {}", display.version_string()); + +        let samples = C::sample_count(&compositor_settings) as u8; +        let mut template = ConfigTemplateBuilder::new() +            .with_surface_type(ConfigSurfaceTypes::WINDOW); + +        if samples != 0 { +            template = template.with_multisampling(samples); +        } + +        #[cfg(all(windows, not(target_arch = "wasm32")))] +        let template = template.compatible_with_native_window(window_handle); + +        log::debug!("Searching for display configurations"); +        let configuration = display +            .find_configs(template.build()) +            .map_err(|_| { +                Error::GraphicsCreationFailed( +                    iced_graphics::Error::NoAvailablePixelFormat, +                ) +            })? +            .map(Configuration) +            .inspect(|config| { +                log::trace!("{config:#?}"); +            }) +            .min_by_key(|config| { +                config.as_ref().num_samples().saturating_sub(samples) +            }) +            .ok_or(Error::GraphicsCreationFailed( +                iced_graphics::Error::NoAvailablePixelFormat, +            ))?; + +        log::debug!("Selected: {configuration:#?}"); + +        #[cfg(all( +            unix, +            not(target_os = "macos"), +            not(target_arch = "wasm32") +        ))] +        let (window, window_handle) = { +            use glutin::platform::x11::X11GlConfigExt; +            let builder = +                if let Some(visual) = configuration.as_ref().x11_visual() { +                    use winit::platform::unix::WindowBuilderExtUnix; +                    builder.with_x11_visual(visual.into_raw()) +                } else { +                    builder +                }; + +            let window = builder +                .build(&event_loop) +                .map_err(Error::WindowCreationFailed)?; + +            let handle = window.raw_window_handle(); -        let (first_builder, second_builder) = if settings.try_opengles_first { -            (opengles_builder, opengl_builder) -        } else { -            (opengl_builder, opengles_builder) +            (window, handle)          }; -        log::info!("Trying first builder: {:#?}", first_builder); +        let attributes = +            ContextAttributesBuilder::new().build(Some(window_handle)); +        let fallback_attributes = ContextAttributesBuilder::new() +            .with_context_api(ContextApi::Gles(None)) +            .build(Some(window_handle)); -        let context = first_builder -            .build_windowed(builder.clone(), &event_loop) +        let context = display +            .create_context(configuration.as_ref(), &attributes)              .or_else(|_| { -                log::info!("Trying second builder: {:#?}", second_builder); -                second_builder.build_windowed(builder, &event_loop) +                display.create_context( +                    configuration.as_ref(), +                    &fallback_attributes, +                )              })              .map_err(|error| { -                use glutin::CreationError; -                use iced_graphics::Error as ContextError; +                Error::GraphicsCreationFailed( +                    iced_graphics::Error::BackendError(format!( +                        "failed to create context: {error}" +                    )), +                ) +            })?; -                match error { -                    CreationError::Window(error) => { -                        Error::WindowCreationFailed(error) -                    } -                    CreationError::OpenGlVersionNotSupported => { -                        Error::GraphicsCreationFailed( -                            ContextError::VersionNotSupported, -                        ) -                    } -                    CreationError::NoAvailablePixelFormat => { -                        Error::GraphicsCreationFailed( -                            ContextError::NoAvailablePixelFormat, -                        ) -                    } -                    error => Error::GraphicsCreationFailed( -                        ContextError::BackendError(error.to_string()), -                    ), -                } +        let (width, height) = window.inner_size().into(); +        let surface_attributes = +            SurfaceAttributesBuilder::<WindowSurface>::new() +                .with_srgb(Some(true)) +                .build( +                    window_handle, +                    NonZeroU32::new(width).unwrap_or(ONE), +                    NonZeroU32::new(height).unwrap_or(ONE), +                ); + +        let surface = display +            .create_window_surface(configuration.as_ref(), &surface_attributes) +            .map_err(|error| { +                Error::GraphicsCreationFailed( +                    iced_graphics::Error::BackendError(format!( +                        "failed to create surface: {error}" +                    )), +                )              })?; -        #[allow(unsafe_code)] -        unsafe { -            let (context, window) = context.split(); +        let context = { +            context +                .make_current(&surface) +                .expect("make context current") +        }; -            ( -                context -                    .make_current(&window) -                    .expect("Make OpenGL context current"), -                window, -            ) +        if let Err(error) = surface.set_swap_interval( +            &context, +            glutin::surface::SwapInterval::Wait(ONE), +        ) { +            log::error!("set swap interval failed: {}", error);          } + +        (display, window, configuration.0, surface, context)      };      #[allow(unsafe_code)]      let (compositor, renderer) = unsafe {          C::new(compositor_settings, |address| { -            context.get_proc_address(address) +            let address = CString::new(address).expect("address error"); +            display.get_proc_address(address.as_c_str())          })?      }; +    let context = { context.make_not_current().expect("make context current") }; +      let (mut sender, receiver) = mpsc::unbounded();      let mut instance = Box::pin(run_instance::<A, E, C>( @@ -143,8 +296,11 @@ where          proxy,          debug,          receiver, -        context, +        display,          window, +        configuration, +        surface, +        context,          init_command,          settings.exit_on_close_request,      )); @@ -152,25 +308,25 @@ where      let mut context = task::Context::from_waker(task::noop_waker_ref());      let _ = event_loop.run_return(move |event, event_loop, control_flow| { -        use glutin::event_loop::ControlFlow; +        use winit::event_loop::ControlFlow;          if let ControlFlow::ExitWithCode(_) = control_flow {              return;          }          let event = match event { -            glutin::event::Event::WindowEvent { +            winit::event::Event::WindowEvent {                  event: -                    glutin::event::WindowEvent::ScaleFactorChanged { +                    winit::event::WindowEvent::ScaleFactorChanged {                          new_inner_size,                          ..                      },                  window_id, -            } => Some(glutin::event::Event::WindowEvent { -                event: glutin::event::WindowEvent::Resized(*new_inner_size), +            } => Some(winit::event::Event::WindowEvent { +                event: winit::event::WindowEvent::Resized(*new_inner_size),                  window_id,              }), -            glutin::event::Event::UserEvent(Event::NewWindow(id, settings)) => { +            winit::event::Event::UserEvent(Event::NewWindow(id, settings)) => {                  // TODO(derezzedex)                  let window = settings                      .into_builder( @@ -181,7 +337,7 @@ where                      .build(event_loop)                      .expect("Failed to build window"); -                Some(glutin::event::Event::UserEvent(Event::WindowCreated( +                Some(winit::event::Event::UserEvent(Event::WindowCreated(                      id, window,                  )))              } @@ -208,13 +364,16 @@ async fn run_instance<A, E, C>(      mut compositor: C,      mut renderer: A::Renderer,      mut runtime: Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, -    mut proxy: glutin::event_loop::EventLoopProxy<Event<A::Message>>, +    mut proxy: winit::event_loop::EventLoopProxy<Event<A::Message>>,      mut debug: Debug,      mut receiver: mpsc::UnboundedReceiver< -        glutin::event::Event<'_, Event<A::Message>>, +        winit::event::Event<'_, Event<A::Message>>,      >, -    mut context: glutin::RawContext<glutin::PossiblyCurrent>, -    window: Window, +    display: Display, +    window: winit::window::Window, +    configuration: Config, +    surface: Surface<WindowSurface>, +    context: NotCurrentContext,      init_command: Command<A::Message>,      _exit_on_close_request: bool,  ) where @@ -223,8 +382,14 @@ async fn run_instance<A, E, C>(      C: iced_graphics::window::GLCompositor<Renderer = A::Renderer> + 'static,      <A::Renderer as iced_native::Renderer>::Theme: StyleSheet,  { -    use glutin::event;      use iced_winit::futures::stream::StreamExt; +    use winit::event; + +    let context = { +        context +            .make_current(&surface) +            .expect("make context current") +    };      let mut clipboard = Clipboard::connect(&window);      let mut cache = user_interface::Cache::default(); @@ -241,6 +406,7 @@ async fn run_instance<A, E, C>(      let mut current_context_window = window.id();      let mut window_ids = HashMap::from([(window.id(), window::Id::MAIN)]);      let mut windows = HashMap::from([(window::Id::MAIN, window)]); +    let mut surfaces = HashMap::from([(window::Id::MAIN, surface)]);      let mut states = HashMap::from([(window::Id::MAIN, state)]);      let mut interfaces =          ManuallyDrop::new(HashMap::from([(window::Id::MAIN, user_interface)])); @@ -419,10 +585,32 @@ async fn run_instance<A, E, C>(                          id,                      ); +                    let window_handle = window.raw_window_handle(); +                    let (width, height) = window.inner_size().into(); +                    let surface_attributes = +                        SurfaceAttributesBuilder::<WindowSurface>::new() +                            .with_srgb(Some(true)) +                            .build( +                                window_handle, +                                NonZeroU32::new(width).unwrap_or(ONE), +                                NonZeroU32::new(height).unwrap_or(ONE), +                            ); + +                    #[allow(unsafe_code)] +                    let surface = unsafe { +                        display +                            .create_window_surface( +                                &configuration, +                                &surface_attributes, +                            ) +                            .expect("failed to create surface") +                    }; +                      let _ = states.insert(id, state);                      let _ = interfaces.insert(id, user_interface);                      let _ = window_ids.insert(window.id(), id);                      let _ = windows.insert(id, window); +                    let _ = surfaces.insert(id, surface);                  }                  Event::CloseWindow(id) => {                      // TODO(derezzedex): log errors @@ -437,6 +625,9 @@ async fn run_instance<A, E, C>(                      if interfaces.remove(&id).is_none() {                          println!("Failed to remove from `interfaces`!");                      } +                    if surfaces.remove(&id).is_none() { +                        println!("Failed to remove from `surfaces`!") +                    }                      if windows.remove(&id).is_none() {                          println!("Failed to remove from `windows`!")                      } @@ -455,17 +646,19 @@ async fn run_instance<A, E, C>(                  let window =                      window_ids.get(&id).and_then(|id| windows.get(id)).unwrap(); +                let surface = window_ids +                    .get(&id) +                    .and_then(|id| surfaces.get(id)) +                    .unwrap(); +                  debug.render_started(); -                #[allow(unsafe_code)] -                unsafe { -                    if current_context_window != id { -                        context = context -                            .make_current(window) -                            .expect("Make OpenGL context current"); +                if current_context_window != id { +                    context +                        .make_current(&surface) +                        .expect("Make OpenGL context current"); -                        current_context_window = id; -                    } +                    current_context_window = id;                  }                  if state.viewport_changed() { @@ -501,10 +694,11 @@ async fn run_instance<A, E, C>(                          mouse_interaction = new_mouse_interaction;                      } -                    context.resize(glutin::dpi::PhysicalSize::new( -                        physical_size.width, -                        physical_size.height, -                    )); +                    surface.resize( +                        &context, +                        NonZeroU32::new(physical_size.width).unwrap_or(ONE), +                        NonZeroU32::new(physical_size.height).unwrap_or(ONE), +                    );                      compositor.resize_viewport(physical_size); @@ -519,7 +713,7 @@ async fn run_instance<A, E, C>(                      &debug.overlay(),                  ); -                context.swap_buffers(window).expect("Swap buffers"); +                surface.swap_buffers(&context).expect("Swap buffers");                  debug.render_finished(); @@ -595,7 +789,7 @@ pub enum Event<Message> {      /// TODO(derezzedex)      CloseWindow(window::Id),      /// TODO(derezzedex) -    WindowCreated(window::Id, glutin::window::Window), +    WindowCreated(window::Id, winit::window::Window),  }  /// Updates an [`Application`] by feeding it the provided messages, spawning any @@ -607,10 +801,10 @@ pub fn update<A: Application, E: Executor>(      renderer: &mut A::Renderer,      runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,      clipboard: &mut Clipboard, -    proxy: &mut glutin::event_loop::EventLoopProxy<Event<A::Message>>, +    proxy: &mut winit::event_loop::EventLoopProxy<Event<A::Message>>,      debug: &mut Debug,      messages: &mut Vec<A::Message>, -    windows: &HashMap<window::Id, glutin::window::Window>, +    windows: &HashMap<window::Id, winit::window::Window>,      graphics_info: impl FnOnce() -> iced_graphics::compositor::Information + Copy,  ) where      <A::Renderer as crate::Renderer>::Theme: StyleSheet, @@ -650,9 +844,9 @@ pub fn run_command<A, E>(      command: Command<A::Message>,      runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,      clipboard: &mut Clipboard, -    proxy: &mut glutin::event_loop::EventLoopProxy<Event<A::Message>>, +    proxy: &mut winit::event_loop::EventLoopProxy<Event<A::Message>>,      debug: &mut Debug, -    windows: &HashMap<window::Id, glutin::window::Window>, +    windows: &HashMap<window::Id, winit::window::Window>,      _graphics_info: impl FnOnce() -> iced_graphics::compositor::Information + Copy,  ) where      A: Application, @@ -695,14 +889,14 @@ pub fn run_command<A, E>(                  }                  window::Action::Resize { width, height } => {                      let window = windows.get(&id).expect("No window found"); -                    window.set_inner_size(glutin::dpi::LogicalSize { +                    window.set_inner_size(winit::dpi::LogicalSize {                          width,                          height,                      });                  }                  window::Action::Move { x, y } => {                      let window = windows.get(&id).expect("No window found"); -                    window.set_outer_position(glutin::dpi::LogicalPosition { +                    window.set_outer_position(winit::dpi::LogicalPosition {                          x,                          y,                      }); diff --git a/glutin/src/multi_window/state.rs b/glutin/src/multi_window/state.rs index 163f46bd..321fc4d1 100644 --- a/glutin/src/multi_window/state.rs +++ b/glutin/src/multi_window/state.rs @@ -4,9 +4,11 @@ use crate::multi_window::{Application, Event};  use crate::window;  use crate::{Color, Debug, Point, Size, Viewport}; -use glutin::event::{Touch, WindowEvent}; -use glutin::event_loop::EventLoopProxy; -use glutin::window::Window; +use iced_winit::winit; +use winit::event::{Touch, WindowEvent}; +use winit::event_loop::EventLoopProxy; +use winit::window::Window; +  use std::collections::HashMap;  use std::marker::PhantomData; @@ -20,8 +22,8 @@ where      scale_factor: f64,      viewport: Viewport,      viewport_changed: bool, -    cursor_position: glutin::dpi::PhysicalPosition<f64>, -    modifiers: glutin::event::ModifiersState, +    cursor_position: winit::dpi::PhysicalPosition<f64>, +    modifiers: winit::event::ModifiersState,      theme: <A::Renderer as crate::Renderer>::Theme,      appearance: iced_winit::application::Appearance,      application: PhantomData<A>, @@ -53,8 +55,8 @@ where              viewport,              viewport_changed: false,              // TODO: Encode cursor availability in the type-system -            cursor_position: glutin::dpi::PhysicalPosition::new(-1.0, -1.0), -            modifiers: glutin::event::ModifiersState::default(), +            cursor_position: winit::dpi::PhysicalPosition::new(-1.0, -1.0), +            modifiers: winit::event::ModifiersState::default(),              theme,              appearance,              application: PhantomData, @@ -95,7 +97,7 @@ where      }      /// Returns the current keyboard modifiers of the [`State`]. -    pub fn modifiers(&self) -> glutin::event::ModifiersState { +    pub fn modifiers(&self) -> winit::event::ModifiersState {          self.modifiers      } @@ -156,7 +158,7 @@ where              WindowEvent::CursorLeft { .. } => {                  // TODO: Encode cursor availability in the type-system                  self.cursor_position = -                    glutin::dpi::PhysicalPosition::new(-1.0, -1.0); +                    winit::dpi::PhysicalPosition::new(-1.0, -1.0);              }              WindowEvent::ModifiersChanged(new_modifiers) => {                  self.modifiers = *new_modifiers; | 
