diff options
Diffstat (limited to 'glutin')
| -rw-r--r-- | glutin/Cargo.toml | 10 | ||||
| -rw-r--r-- | glutin/src/application.rs | 366 | ||||
| -rw-r--r-- | glutin/src/lib.rs | 3 | ||||
| -rw-r--r-- | glutin/src/multi_window.rs | 1060 | 
4 files changed, 1339 insertions, 100 deletions
| diff --git a/glutin/Cargo.toml b/glutin/Cargo.toml index 10d3778b..01dd3748 100644 --- a/glutin/Cargo.toml +++ b/glutin/Cargo.toml @@ -11,17 +11,19 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]  categories = ["gui"]  [features] -trace = ["iced_winit/trace"] +trace = ["iced_winit/trace", "tracing"]  debug = ["iced_winit/debug"]  system = ["iced_winit/system"] +multi_window = ["iced_winit/multi_window"] + +[dependencies.raw-window-handle] +version = "0.5.0"  [dependencies]  log = "0.4"  [dependencies.glutin] -version = "0.29" -git = "https://github.com/iced-rs/glutin" -rev = "da8d291486b4c9bec12487a46c119c4b1d386abf" +version = "0.30"  [dependencies.iced_native]  version = "0.9" diff --git a/glutin/src/application.rs b/glutin/src/application.rs index 5921bdd0..c0a8cda4 100644 --- a/glutin/src/application.rs +++ b/glutin/src/application.rs @@ -13,14 +13,33 @@ use iced_winit::futures::channel::mpsc;  use iced_winit::renderer;  use iced_winit::time::Instant;  use iced_winit::user_interface; +use iced_winit::winit;  use iced_winit::{Clipboard, Command, Debug, Event, 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::ffi::CString;  use std::mem::ManuallyDrop; +use std::num::NonZeroU32; -#[cfg(feature = "tracing")] +#[cfg(feature = "trace")]  use tracing::{info_span, instrument::Instrument}; +#[allow(unsafe_code)] +const ONE: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(1) }; +  /// Runs an [`Application`] with an executor, compositor, and the provided  /// settings.  pub fn run<A, E, C>( @@ -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;      #[cfg(feature = "trace")]      let _guard = iced_winit::Profiler::init(); @@ -45,7 +63,7 @@ where      let mut debug = Debug::new();      debug.startup_started(); -    #[cfg(feature = "tracing")] +    #[cfg(feature = "trace")]      let _ = info_span!("Application::Glutin", "RUN").entered();      let mut event_loop = EventLoopBuilder::with_user_event().build(); @@ -64,74 +82,205 @@ where          runtime.enter(|| A::new(flags))      }; -    let context = { -        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::debug!("Window builder: {:#?}", builder); +    log::debug!("Window builder: {:#?}", builder); -        let opengl_builder = ContextBuilder::new() -            .with_vsync(true) -            .with_multisampling(C::sample_count(&compositor_settings) as u16); +    #[allow(unsafe_code)] +    let (display, window, 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() +            } +        } -        let opengles_builder = opengl_builder.clone().with_gl( -            glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (2, 0)), -        ); +        impl AsRef<Config> for Configuration { +            fn as_ref(&self) -> &Config { +                &self.0 +            } +        } + +        let display_handle = event_loop.raw_display_handle(); + +        #[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 (first_builder, second_builder) = if settings.try_opengles_first { -            (opengles_builder, opengl_builder) -        } else { -            (opengl_builder, opengles_builder) +            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(); + +            (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 surface = gl_surface(&display, configuration.as_ref(), &window) +            .map_err(|error| { +                Error::GraphicsCreationFailed( +                    iced_graphics::Error::BackendError(format!( +                        "failed to create surface: {error}" +                    )), +                )              })?; -        #[allow(unsafe_code)] -        unsafe { -            context.make_current().expect("Make OpenGL context current") +        let context = { +            context +                .make_current(&surface) +                .expect("make context current") +        }; + +        if let Err(error) = surface.set_swap_interval( +            &context, +            glutin::surface::SwapInterval::Wait(ONE), +        ) { +            log::error!("set swap interval failed: {}", error);          } + +        (display, window, 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 event_sender, event_receiver) = mpsc::unbounded();      let (control_sender, mut control_receiver) = mpsc::unbounded(); @@ -145,12 +294,14 @@ where              debug,              event_receiver,              control_sender, +            window, +            surface,              context,              init_command,              settings.exit_on_close_request,          ); -        #[cfg(feature = "tracing")] +        #[cfg(feature = "trace")]          let run_instance =              run_instance.instrument(info_span!("Application", "LOOP")); @@ -160,22 +311,22 @@ where      let mut context = task::Context::from_waker(task::noop_waker_ref());      let _ = event_loop.run_return(move |event, _, 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,              }),              _ => event.to_static(), @@ -207,13 +358,13 @@ async fn run_instance<A, E, C>(      mut compositor: C,      mut renderer: A::Renderer,      mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, -    mut proxy: glutin::event_loop::EventLoopProxy<A::Message>, +    mut proxy: winit::event_loop::EventLoopProxy<A::Message>,      mut debug: Debug, -    mut event_receiver: mpsc::UnboundedReceiver< -        glutin::event::Event<'_, A::Message>, -    >, -    mut control_sender: mpsc::UnboundedSender<glutin::event_loop::ControlFlow>, -    mut context: glutin::ContextWrapper<glutin::PossiblyCurrent, Window>, +    mut event_receiver: mpsc::UnboundedReceiver<winit::event::Event<'_, A::Message>, >, +    mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>, +    window: winit::window::Window, +    surface: Surface<WindowSurface>, +    context: NotCurrentContext,      init_command: Command<A::Message>,      exit_on_close_request: bool,  ) where @@ -222,13 +373,19 @@ async fn run_instance<A, E, C>(      C: window::GLCompositor<Renderer = A::Renderer> + 'static,      <A::Renderer as iced_native::Renderer>::Theme: StyleSheet,  { -    use glutin::event; -    use glutin::event_loop::ControlFlow;      use iced_winit::futures::stream::StreamExt; +    use winit::event_loop::ControlFlow; +    use winit::event; -    let mut clipboard = Clipboard::connect(context.window()); +    let context = { +        context +            .make_current(&surface) +            .expect("make context current") +    }; + +    let mut clipboard = Clipboard::connect(&window);      let mut cache = user_interface::Cache::default(); -    let mut state = application::State::new(&application, context.window()); +    let mut state = application::State::new(&application, &window);      let mut viewport_version = state.viewport_version();      let mut should_exit = false; @@ -243,7 +400,7 @@ async fn run_instance<A, E, C>(          &mut should_exit,          &mut proxy,          &mut debug, -        context.window(), +        &window,          || compositor.fetch_information(),      );      runtime.track(application.subscription()); @@ -316,12 +473,12 @@ async fn run_instance<A, E, C>(                          &mut proxy,                          &mut debug,                          &mut messages, -                        context.window(), +                        &window,                          || compositor.fetch_information(),                      );                      // Update window -                    state.synchronize(&application, context.window()); +                    state.synchronize(&application, &window);                      user_interface =                          ManuallyDrop::new(application::build_user_interface( @@ -343,6 +500,7 @@ async fn run_instance<A, E, C>(                  // Then, we can use the `interface_state` here to decide if a redraw                  // is needed right away, or simply wait until a specific time.                  let redraw_event = Event::Window( +                    crate::window::Id::MAIN,                      crate::window::Event::RedrawRequested(Instant::now()),                  ); @@ -366,16 +524,15 @@ async fn run_instance<A, E, C>(                  debug.draw_finished();                  if new_mouse_interaction != mouse_interaction { -                    context.window().set_cursor_icon( -                        conversion::mouse_interaction(new_mouse_interaction), -                    ); +                    window.set_cursor_icon(conversion::mouse_interaction( +                        new_mouse_interaction, +                    ));                      mouse_interaction = new_mouse_interaction;                  } -                context.window().request_redraw(); -                runtime -                    .broadcast((redraw_event, crate::event::Status::Ignored)); +                window.request_redraw(); +                runtime.broadcast((redraw_event, crate::event::Status::Ignored));                  let _ = control_sender.start_send(match interface_state {                      user_interface::State::Updated { @@ -407,18 +564,15 @@ async fn run_instance<A, E, C>(                  messages.push(message);              }              event::Event::RedrawRequested(_) => { -                #[cfg(feature = "tracing")] +                #[cfg(feature = "trace")]                  let _ = info_span!("Application", "FRAME").entered();                  debug.render_started(); -                #[allow(unsafe_code)] -                unsafe { -                    if !context.is_current() { -                        context = context -                            .make_current() -                            .expect("Make OpenGL context current"); -                    } +                if !context.is_current() { +                    context +                        .make_current(&surface) +                        .expect("Make OpenGL context current");                  }                  let current_viewport_version = state.viewport_version(); @@ -446,19 +600,18 @@ async fn run_instance<A, E, C>(                      debug.draw_finished();                      if new_mouse_interaction != mouse_interaction { -                        context.window().set_cursor_icon( -                            conversion::mouse_interaction( -                                new_mouse_interaction, -                            ), -                        ); +                        window.set_cursor_icon(conversion::mouse_interaction( +                            new_mouse_interaction, +                        ));                          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); @@ -472,7 +625,7 @@ async fn run_instance<A, E, C>(                      &debug.overlay(),                  ); -                context.swap_buffers().expect("Swap buffers"); +                surface.swap_buffers(&context).expect("Swap buffers");                  debug.render_finished(); @@ -489,9 +642,10 @@ async fn run_instance<A, E, C>(                      break;                  } -                state.update(context.window(), &window_event, &mut debug); +                state.update(&window, &window_event, &mut debug);                  if let Some(event) = conversion::window_event( +                    crate::window::Id::MAIN,                      &window_event,                      state.scale_factor(),                      state.modifiers(), @@ -506,3 +660,23 @@ async fn run_instance<A, E, C>(      // Manually drop the user interface      drop(ManuallyDrop::into_inner(user_interface));  } + +#[allow(unsafe_code)] +/// Creates a new [`glutin::Surface<WindowSurface>`]. +pub fn gl_surface( +    display: &Display, +    gl_config: &Config, +    window: &winit::window::Window, +) -> Result<Surface<WindowSurface>, glutin::error::Error> { +    let (width, height) = window.inner_size().into(); + +    let surface_attributes = SurfaceAttributesBuilder::<WindowSurface>::new() +        .with_srgb(Some(true)) +        .build( +            window.raw_window_handle(), +            NonZeroU32::new(width).unwrap_or(ONE), +            NonZeroU32::new(height).unwrap_or(ONE), +        ); + +    unsafe { display.create_window_surface(gl_config, &surface_attributes) } +} diff --git a/glutin/src/lib.rs b/glutin/src/lib.rs index 33afd664..45d6cb5b 100644 --- a/glutin/src/lib.rs +++ b/glutin/src/lib.rs @@ -29,5 +29,8 @@ pub use iced_winit::*;  pub mod application; +#[cfg(feature = "multi_window")] +pub mod multi_window; +  #[doc(no_inline)]  pub use application::Application; diff --git a/glutin/src/multi_window.rs b/glutin/src/multi_window.rs new file mode 100644 index 00000000..620d01d8 --- /dev/null +++ b/glutin/src/multi_window.rs @@ -0,0 +1,1060 @@ +//! Create interactive, native cross-platform applications. +use crate::mouse; +use crate::{Error, Executor, Runtime}; + +pub use iced_winit::multi_window::{self, Application, StyleSheet}; + +use iced_winit::conversion; +use iced_winit::futures; +use iced_winit::futures::channel::mpsc; +use iced_winit::renderer; +use iced_winit::user_interface; +use iced_winit::window; +use iced_winit::winit; +use iced_winit::{Clipboard, Command, Debug, Proxy, Settings}; + +use glutin::config::{ +    Config, ConfigSurfaceTypes, ConfigTemplateBuilder, GlConfig, +}; +use glutin::context::{ +    ContextApi, ContextAttributesBuilder, NotCurrentContext, +    NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext, +}; +use glutin::display::{Display, DisplayApiPreference, GlDisplay}; +use glutin::surface::{GlSurface, SwapInterval}; +use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; + +use crate::application::gl_surface; +use iced_winit::multi_window::Event; +use std::collections::HashMap; +use std::ffi::CString; +use std::mem::ManuallyDrop; +use std::num::NonZeroU32; + +use iced_native::widget::operation; +#[cfg(feature = "tracing")] +use tracing::{info_span, instrument::Instrument}; + +#[allow(unsafe_code)] +const ONE: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(1) }; + +/// Runs an [`Application`] with an executor, compositor, and the provided +/// settings. +pub fn run<A, E, C>( +    settings: Settings<A::Flags>, +    compositor_settings: C::Settings, +) -> Result<(), Error> +where +    A: Application + 'static, +    E: Executor + 'static, +    C: iced_graphics::window::GLCompositor<Renderer = A::Renderer> + 'static, +    <A::Renderer as iced_native::Renderer>::Theme: StyleSheet, +{ +    use futures::task; +    use futures::Future; +    use winit::event_loop::EventLoopBuilder; +    use winit::platform::run_return::EventLoopExtRunReturn; + +    #[cfg(feature = "trace")] +    let _guard = iced_winit::Profiler::init(); + +    let mut debug = Debug::new(); +    debug.startup_started(); + +    #[cfg(feature = "tracing")] +    let _ = info_span!("Application::Glutin", "RUN").entered(); + +    let mut event_loop = EventLoopBuilder::with_user_event().build(); +    let proxy = event_loop.create_proxy(); + +    let runtime = { +        let executor = E::new().map_err(Error::ExecutorCreationFailed)?; +        let proxy = Proxy::new(event_loop.create_proxy()); + +        Runtime::new(executor, proxy) +    }; + +    let (application, init_command) = { +        let flags = settings.flags; + +        runtime.enter(|| A::new(flags)) +    }; + +    let builder = settings.window.into_builder( +        &application.title(window::Id::MAIN), +        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() +            } +        } + +        impl AsRef<Config> for Configuration { +            fn as_ref(&self) -> &Config { +                &self.0 +            } +        } + +        let display_handle = event_loop.raw_display_handle(); + +        #[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(); + +            (window, handle) +        }; + +        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 = display +            .create_context(configuration.as_ref(), &attributes) +            .or_else(|_| { +                display.create_context( +                    configuration.as_ref(), +                    &fallback_attributes, +                ) +            }) +            .map_err(|error| { +                Error::GraphicsCreationFailed( +                    iced_graphics::Error::BackendError(format!( +                        "failed to create context: {error}" +                    )), +                ) +            })?; + +        let surface = gl_surface(&display, configuration.as_ref(), &window) +            .map_err(|error| { +                Error::GraphicsCreationFailed( +                    iced_graphics::Error::BackendError(format!( +                        "failed to create surface: {error}" +                    )), +                ) +            })?; + +        (display, window, configuration.0, surface, context) +    }; + +    let windows: HashMap<window::Id, winit::window::Window> = +        HashMap::from([(window::Id::MAIN, window)]); + +    // need to make context current before trying to load GL functions +    let context = context +        .make_current(&surface) +        .expect("Make context current."); + +    #[allow(unsafe_code)] +    let (compositor, renderer) = unsafe { +        C::new(compositor_settings, |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 not current."); + +    let (mut sender, receiver) = mpsc::unbounded(); + +    let mut instance = Box::pin({ +        let run_instance = run_instance::<A, E, C>( +            application, +            compositor, +            renderer, +            runtime, +            proxy, +            debug, +            receiver, +            display, +            windows, +            configuration, +            context, +            init_command, +            settings.exit_on_close_request, +        ); + +        #[cfg(feature = "tracing")] +        let run_instance = +            run_instance.instrument(info_span!("Application", "LOOP")); + +        run_instance +    }); + +    let mut context = task::Context::from_waker(task::noop_waker_ref()); + +    let _ = event_loop.run_return(move |event, event_loop, control_flow| { +        use winit::event_loop::ControlFlow; + +        if let ControlFlow::ExitWithCode(_) = control_flow { +            return; +        } + +        let event = match event { +            winit::event::Event::WindowEvent { +                event: +                    winit::event::WindowEvent::ScaleFactorChanged { +                        new_inner_size, +                        .. +                    }, +                window_id, +            } => Some(winit::event::Event::WindowEvent { +                event: winit::event::WindowEvent::Resized(*new_inner_size), +                window_id, +            }), +            winit::event::Event::UserEvent(Event::NewWindow { +                id, +                settings, +                title, +            }) => { +                let window = settings +                    .into_builder(&title, event_loop.primary_monitor(), None) +                    .build(event_loop) +                    .expect("Failed to build window"); + +                Some(winit::event::Event::UserEvent(Event::WindowCreated( +                    id, window, +                ))) +            } +            _ => event.to_static(), +        }; + +        if let Some(event) = event { +            sender.start_send(event).expect("Send event"); + +            let poll = instance.as_mut().poll(&mut context); + +            *control_flow = match poll { +                task::Poll::Pending => ControlFlow::Wait, +                task::Poll::Ready(_) => ControlFlow::Exit, +            }; +        } +    }); + +    Ok(()) +} + +async fn run_instance<A, E, C>( +    mut application: A, +    mut compositor: C, +    mut renderer: A::Renderer, +    mut runtime: Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, +    mut proxy: winit::event_loop::EventLoopProxy<Event<A::Message>>, +    mut debug: Debug, +    mut receiver: mpsc::UnboundedReceiver< +        winit::event::Event<'_, Event<A::Message>>, +    >, +    display: Display, +    mut windows: HashMap<window::Id, winit::window::Window>, +    configuration: Config, +    mut context: NotCurrentContext, +    init_command: Command<A::Message>, +    _exit_on_close_request: bool, +) where +    A: Application + 'static, +    E: Executor + 'static, +    C: iced_graphics::window::GLCompositor<Renderer = A::Renderer> + 'static, +    <A::Renderer as iced_native::Renderer>::Theme: StyleSheet, +{ +    use iced_winit::futures::stream::StreamExt; +    use winit::event; + +    let mut clipboard = +        Clipboard::connect(windows.values().next().expect("No window found")); +    let mut caches = HashMap::new(); +    let mut current_context_window = None; +    let mut window_ids: HashMap<_, _> = windows +        .iter() +        .map(|(&id, window)| (window.id(), id)) +        .collect(); +    let mut states = HashMap::new(); +    let mut surfaces = HashMap::new(); +    let mut interfaces = ManuallyDrop::new(HashMap::new()); + +    for (&id, window) in windows.keys().zip(windows.values()) { +        let surface = gl_surface(&display, &configuration, &window) +            .expect("Create surface."); +        let current_context = +            context.make_current(&surface).expect("Make current."); +        let state = multi_window::State::new(&application, id, &window); +        let physical_size = state.physical_size(); + +        surface.resize( +            ¤t_context, +            NonZeroU32::new(physical_size.width).unwrap_or(ONE), +            NonZeroU32::new(physical_size.height).unwrap_or(ONE), +        ); + +        let user_interface = multi_window::build_user_interface( +            &application, +            user_interface::Cache::default(), +            &mut renderer, +            state.logical_size(), +            &mut debug, +            id, +        ); + +        context = current_context +            .make_not_current() +            .expect("Make not current."); + +        let _ = states.insert(id, state); +        let _ = surfaces.insert(id, surface); +        let _ = interfaces.insert(id, user_interface); +    } + +    run_command( +        &application, +        &mut caches, +        &states, +        &mut renderer, +        init_command, +        &mut runtime, +        &mut clipboard, +        &mut proxy, +        &mut debug, +        &windows, +        || compositor.fetch_information(), +    ); + +    runtime.track(application.subscription().map(Event::Application)); + +    let mut mouse_interaction = mouse::Interaction::default(); +    let mut events = Vec::new(); +    let mut messages = Vec::new(); + +    debug.startup_finished(); + +    'main: while let Some(event) = receiver.next().await { +        match event { +            event::Event::MainEventsCleared => { +                for id in windows.keys().copied() { +                    let (filtered, remaining): (Vec<_>, Vec<_>) = +                        events.iter().cloned().partition( +                            |(window_id, _event): &( +                                Option<window::Id>, +                                iced_native::event::Event, +                            )| { +                                *window_id == Some(id) || *window_id == None +                            }, +                        ); + +                    events.retain(|el| remaining.contains(el)); +                    let filtered: Vec<_> = filtered +                        .into_iter() +                        .map(|(_id, event)| event) +                        .collect(); + +                    let cursor_position = +                        states.get(&id).unwrap().cursor_position(); +                    let window = windows.get(&id).unwrap(); + +                    if filtered.is_empty() && messages.is_empty() { +                        continue; +                    } + +                    debug.event_processing_started(); + +                    let (interface_state, statuses) = { +                        let user_interface = interfaces.get_mut(&id).unwrap(); +                        user_interface.update( +                            &filtered, +                            cursor_position, +                            &mut renderer, +                            &mut clipboard, +                            &mut messages, +                        ) +                    }; + +                    debug.event_processing_finished(); + +                    for event in filtered.into_iter().zip(statuses.into_iter()) +                    { +                        runtime.broadcast(event); +                    } + +                    if !messages.is_empty() +                        || matches!( +                            interface_state, +                            user_interface::State::Outdated +                        ) +                    { +                        let user_interfaces: HashMap<_, _> = +                            ManuallyDrop::into_inner(interfaces) +                                .drain() +                                .map(|(id, interface)| { +                                    (id, interface.into_cache()) +                                }) +                                .collect(); + +                        // Update application +                        update( +                            &mut application, +                            &mut caches, +                            &states, +                            &mut renderer, +                            &mut runtime, +                            &mut clipboard, +                            &mut proxy, +                            &mut debug, +                            &mut messages, +                            &windows, +                            || compositor.fetch_information(), +                        ); + +                        // Update window +                        states.get_mut(&id).unwrap().synchronize( +                            &application, +                            id, +                            windows.get(&id).expect("No window found with ID."), +                        ); + +                        let should_exit = application.should_exit(); + +                        interfaces = ManuallyDrop::new(build_user_interfaces( +                            &application, +                            &mut renderer, +                            &mut debug, +                            &states, +                            user_interfaces, +                        )); + +                        if should_exit { +                            break 'main; +                        } +                    } + +                    debug.draw_started(); +                    let new_mouse_interaction = { +                        let user_interface = interfaces.get_mut(&id).unwrap(); +                        let state = states.get(&id).unwrap(); + +                        user_interface.draw( +                            &mut renderer, +                            state.theme(), +                            &renderer::Style { +                                text_color: state.text_color(), +                            }, +                            state.cursor_position(), +                        ) +                    }; +                    debug.draw_finished(); + +                    if new_mouse_interaction != mouse_interaction { +                        window.set_cursor_icon(conversion::mouse_interaction( +                            new_mouse_interaction, +                        )); + +                        mouse_interaction = new_mouse_interaction; +                    } + +                    window.request_redraw(); +                } +            } +            event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( +                event::MacOS::ReceivedUrl(url), +            )) => { +                use iced_native::event; +                events.push(( +                    None, +                    iced_native::Event::PlatformSpecific( +                        event::PlatformSpecific::MacOS( +                            event::MacOS::ReceivedUrl(url), +                        ), +                    ), +                )); +            } +            event::Event::UserEvent(event) => match event { +                Event::Application(message) => messages.push(message), +                Event::WindowCreated(id, window) => { +                    let state = +                        multi_window::State::new(&application, id, &window); +                    let user_interface = multi_window::build_user_interface( +                        &application, +                        user_interface::Cache::default(), +                        &mut renderer, +                        state.logical_size(), +                        &mut debug, +                        id, +                    ); + +                    let surface = gl_surface(&display, &configuration, &window) +                        .expect("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 +                    if let Some(window) = windows.get(&id) { +                        if window_ids.remove(&window.id()).is_none() { +                            println!("Failed to remove from `window_ids`!"); +                        } +                    } +                    if states.remove(&id).is_none() { +                        println!("Failed to remove from `states`!") +                    } +                    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`!") +                    } + +                    if windows.is_empty() { +                        break 'main; +                    } +                } +                Event::NewWindow { .. } => unreachable!(), +            }, +            event::Event::RedrawRequested(id) => { +                #[cfg(feature = "tracing")] +                let _ = info_span!("Application", "FRAME").entered(); + +                let state = window_ids +                    .get(&id) +                    .and_then(|id| states.get_mut(id)) +                    .unwrap(); +                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(); + +                let current_context = +                    context.make_current(&surface).expect("Make current."); + +                if current_context_window != Some(id) { +                    current_context_window = Some(id); +                } + +                if state.viewport_changed() { +                    let physical_size = state.physical_size(); +                    let logical_size = state.logical_size(); + +                    let mut user_interface = window_ids +                        .get(&id) +                        .and_then(|id| interfaces.remove(id)) +                        .unwrap(); + +                    debug.layout_started(); +                    user_interface = +                        user_interface.relayout(logical_size, &mut renderer); +                    debug.layout_finished(); + +                    debug.draw_started(); +                    let new_mouse_interaction = user_interface.draw( +                        &mut renderer, +                        state.theme(), +                        &renderer::Style { +                            text_color: state.text_color(), +                        }, +                        state.cursor_position(), +                    ); +                    debug.draw_finished(); + +                    if new_mouse_interaction != mouse_interaction { +                        window.set_cursor_icon(conversion::mouse_interaction( +                            new_mouse_interaction, +                        )); + +                        mouse_interaction = new_mouse_interaction; +                    } + +                    surface.resize( +                        ¤t_context, +                        NonZeroU32::new(physical_size.width).unwrap_or(ONE), +                        NonZeroU32::new(physical_size.height).unwrap_or(ONE), +                    ); + +                    if let Err(_) = surface.set_swap_interval( +                        ¤t_context, +                        SwapInterval::Wait(ONE), +                    ) { +                        log::error!("Could not set swap interval for surface attached to window id: {:?}", id); +                    } + +                    compositor.resize_viewport(physical_size); + +                    let _ = interfaces +                        .insert(*window_ids.get(&id).unwrap(), user_interface); +                } + +                compositor.present( +                    &mut renderer, +                    state.viewport(), +                    state.background_color(), +                    &debug.overlay(), +                ); + +                surface +                    .swap_buffers(¤t_context) +                    .expect("Swap buffers"); + +                context = current_context +                    .make_not_current() +                    .expect("Make not current."); +                debug.render_finished(); +                // TODO: Handle animations! +                // Maybe we can use `ControlFlow::WaitUntil` for this. +            } +            event::Event::WindowEvent { +                event: window_event, +                window_id, +            } => { +                // dbg!(window_id); +                if let Some(window) = +                    window_ids.get(&window_id).and_then(|id| windows.get(id)) +                { +                    if let Some(state) = window_ids +                        .get(&window_id) +                        .and_then(|id| states.get_mut(id)) +                    { +                        if multi_window::requests_exit( +                            &window_event, +                            state.modifiers(), +                        ) { +                            if let Some(id) = +                                window_ids.get(&window_id).cloned() +                            { +                                let message = application.close_requested(id); +                                messages.push(message); +                            } +                        } + +                        state.update(window, &window_event, &mut debug); + +                        if let Some(event) = conversion::window_event( +                            *window_ids.get(&window_id).unwrap(), +                            &window_event, +                            state.scale_factor(), +                            state.modifiers(), +                        ) { +                            events.push(( +                                window_ids.get(&window_id).cloned(), +                                event, +                            )); +                        } +                    } else { +                        log::error!( +                            "Window state not found for id: {:?}", +                            window_id +                        ); +                    } +                } else { +                    log::error!("Window not found for id: {:?}", window_id); +                } +            } +            _ => {} +        } +    } + +    // Manually drop the user interface +    // drop(ManuallyDrop::into_inner(user_interface)); +} + +/// Updates an [`Application`] by feeding it the provided messages, spawning any +/// resulting [`Command`], and tracking its [`Subscription`]. +pub fn update<A: Application, E: Executor>( +    application: &mut A, +    caches: &mut HashMap<window::Id, user_interface::Cache>, +    states: &HashMap<window::Id, multi_window::State<A>>, +    renderer: &mut A::Renderer, +    runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, +    clipboard: &mut Clipboard, +    proxy: &mut winit::event_loop::EventLoopProxy<Event<A::Message>>, +    debug: &mut Debug, +    messages: &mut Vec<A::Message>, +    windows: &HashMap<window::Id, winit::window::Window>, +    graphics_info: impl FnOnce() -> iced_graphics::compositor::Information + Copy, +) where +    A: Application + 'static, +    <A::Renderer as crate::Renderer>::Theme: StyleSheet, +{ +    for message in messages.drain(..) { +        debug.log_message(&message); + +        debug.update_started(); +        let command = runtime.enter(|| application.update(message)); +        debug.update_finished(); + +        run_command( +            application, +            caches, +            &states, +            renderer, +            command, +            runtime, +            clipboard, +            proxy, +            debug, +            windows, +            graphics_info, +        ); +    } + +    let subscription = application.subscription().map(Event::Application); +    runtime.track(subscription); +} + +/// Runs the actions of a [`Command`]. +pub fn run_command<A, E>( +    application: &A, +    caches: &mut HashMap<window::Id, user_interface::Cache>, +    states: &HashMap<window::Id, multi_window::State<A>>, +    renderer: &mut A::Renderer, +    command: Command<A::Message>, +    runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, +    clipboard: &mut Clipboard, +    proxy: &mut winit::event_loop::EventLoopProxy<Event<A::Message>>, +    debug: &mut Debug, +    windows: &HashMap<window::Id, winit::window::Window>, +    _graphics_info: impl FnOnce() -> iced_graphics::compositor::Information + Copy, +) where +    A: Application + 'static, +    E: Executor, +    <A::Renderer as crate::Renderer>::Theme: StyleSheet, +{ +    use iced_native::command; +    use iced_native::system; +    use iced_native::window; +    use iced_winit::clipboard; +    use iced_winit::futures::FutureExt; + +    for action in command.actions() { +        match action { +            command::Action::Future(future) => { +                runtime.spawn(Box::pin(future.map(Event::Application))); +            } +            command::Action::Clipboard(action) => match action { +                clipboard::Action::Read(tag) => { +                    let message = tag(clipboard.read()); + +                    proxy +                        .send_event(Event::Application(message)) +                        .expect("Send message to event loop"); +                } +                clipboard::Action::Write(contents) => { +                    clipboard.write(contents); +                } +            }, +            command::Action::Window(id, action) => match action { +                window::Action::Spawn { settings } => { +                    proxy +                        .send_event(Event::NewWindow { +                            id, +                            settings: settings.into(), +                            title: application.title(id), +                        }) +                        .expect("Send message to event loop"); +                } +                window::Action::Close => { +                    proxy +                        .send_event(Event::CloseWindow(id)) +                        .expect("Send message to event loop"); +                } +                window::Action::Drag => { +                    let window = windows.get(&id).expect("No window found"); +                    let _res = window.drag_window(); +                } +                window::Action::Resize { width, height } => { +                    let window = windows.get(&id).expect("No window found"); +                    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(winit::dpi::LogicalPosition { +                        x, +                        y, +                    }); +                } +                window::Action::ChangeMode(mode) => { +                    let window = windows.get(&id).expect("No window found"); +                    window.set_visible(conversion::visible(mode)); +                    window.set_fullscreen(conversion::fullscreen( +                        window.primary_monitor(), +                        mode, +                    )); +                } +                window::Action::FetchMode(tag) => { +                    let window = windows.get(&id).expect("No window found"); +                    let mode = if window.is_visible().unwrap_or(true) { +                        conversion::mode(window.fullscreen()) +                    } else { +                        window::Mode::Hidden +                    }; + +                    proxy +                        .send_event(Event::Application(tag(mode))) +                        .expect("Send message to event loop"); +                } +                window::Action::Maximize(value) => { +                    let window = windows.get(&id).expect("No window found!"); +                    window.set_maximized(value); +                } +                window::Action::Minimize(value) => { +                    let window = windows.get(&id).expect("No window found!"); +                    window.set_minimized(value); +                } +                window::Action::ToggleMaximize => { +                    let window = windows.get(&id).expect("No window found!"); +                    window.set_maximized(!window.is_maximized()); +                } +                window::Action::ToggleDecorations => { +                    let window = windows.get(&id).expect("No window found!"); +                    window.set_decorations(!window.is_decorated()); +                } +                window::Action::RequestUserAttention(attention_type) => { +                    let window = windows.get(&id).expect("No window found!"); +                    window.request_user_attention( +                        attention_type.map(conversion::user_attention), +                    ); +                } +                window::Action::GainFocus => { +                    let window = windows.get(&id).expect("No window found!"); +                    window.focus_window(); +                } +                window::Action::ChangeAlwaysOnTop(on_top) => { +                    let window = windows.get(&id).expect("No window found!"); +                    window.set_always_on_top(on_top); +                } +                window::Action::FetchId(tag) => { +                    let window = windows.get(&id).expect("No window found!"); + +                    proxy +                        .send_event(Event::Application(tag(window.id().into()))) +                        .expect("Send message to event loop.") +                } +            }, +            command::Action::System(action) => match action { +                system::Action::QueryInformation(_tag) => { +                    #[cfg(feature = "iced_winit/system")] +                    { +                        let graphics_info = _graphics_info(); +                        let proxy = proxy.clone(); + +                        let _ = std::thread::spawn(move || { +                            let information = +                                crate::system::information(graphics_info); + +                            let message = _tag(information); + +                            proxy +                                .send_event(Event::Application(message)) +                                .expect("Send message to event loop") +                        }); +                    } +                } +            }, +            command::Action::Widget(action) => { +                let mut current_caches = std::mem::take(caches); +                let mut current_operation = Some(action.into_operation()); + +                let mut user_interfaces = multi_window::build_user_interfaces( +                    application, +                    renderer, +                    debug, +                    states, +                    current_caches, +                ); + +                while let Some(mut operation) = current_operation.take() { +                    for user_interface in user_interfaces.values_mut() { +                        user_interface.operate(renderer, operation.as_mut()); + +                        match operation.finish() { +                            operation::Outcome::None => {} +                            operation::Outcome::Some(message) => { +                                proxy +                                    .send_event(Event::Application(message)) +                                    .expect("Send message to event loop"); +                            } +                            operation::Outcome::Chain(next) => { +                                current_operation = Some(next); +                            } +                        } +                    } +                } + +                let user_interfaces: HashMap<_, _> = user_interfaces +                    .drain() +                    .map(|(id, interface)| (id, interface.into_cache())) +                    .collect(); + +                current_caches = user_interfaces; +                *caches = current_caches; +            } +        } +    } +} + +/// TODO(derezzedex) +pub fn build_user_interfaces<'a, A>( +    application: &'a A, +    renderer: &mut A::Renderer, +    debug: &mut Debug, +    states: &HashMap<window::Id, multi_window::State<A>>, +    mut user_interfaces: HashMap<window::Id, user_interface::Cache>, +) -> HashMap< +    window::Id, +    iced_winit::UserInterface< +        'a, +        <A as Application>::Message, +        <A as Application>::Renderer, +    >, +> +where +    A: Application + 'static, +    <A::Renderer as crate::Renderer>::Theme: StyleSheet, +{ +    let mut interfaces = HashMap::new(); + +    for (id, pure_state) in user_interfaces.drain() { +        let state = &states.get(&id).unwrap(); + +        let user_interface = multi_window::build_user_interface( +            application, +            pure_state, +            renderer, +            state.logical_size(), +            debug, +            id, +        ); + +        let _ = interfaces.insert(id, user_interface); +    } + +    interfaces +} | 
