diff options
Diffstat (limited to 'glutin')
| -rw-r--r-- | glutin/Cargo.toml | 30 | ||||
| -rw-r--r-- | glutin/README.md | 29 | ||||
| -rw-r--r-- | glutin/src/application.rs | 297 | ||||
| -rw-r--r-- | glutin/src/lib.rs | 25 | 
4 files changed, 381 insertions, 0 deletions
| diff --git a/glutin/Cargo.toml b/glutin/Cargo.toml new file mode 100644 index 00000000..505ee7e5 --- /dev/null +++ b/glutin/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "iced_glutin" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] +edition = "2018" +description = "A glutin runtime for Iced" +license = "MIT" +repository = "https://github.com/hecrj/iced" +documentation = "https://docs.rs/iced_glutin" +keywords = ["gui", "ui", "graphics", "interface", "widgets"] +categories = ["gui"] + +[features] +debug = ["iced_winit/debug"] + +[dependencies] +glutin = "0.26" + +[dependencies.iced_native] +version = "0.3" +path = "../native" + +[dependencies.iced_winit] +version = "0.2" +path = "../winit" + +[dependencies.iced_graphics] +version = "0.1" +path = "../graphics" +features = ["opengl"] diff --git a/glutin/README.md b/glutin/README.md new file mode 100644 index 00000000..addb9228 --- /dev/null +++ b/glutin/README.md @@ -0,0 +1,29 @@ +# `iced_glutin` +[][documentation] +[](https://crates.io/crates/iced_glutin) +[](https://github.com/hecrj/iced/blob/master/LICENSE) +[](https://iced.zulipchat.com) + +`iced_glutin` offers some convenient abstractions on top of [`iced_native`] to quickstart development when using [`glutin`]. + +It exposes a renderer-agnostic `Application` trait that can be implemented and then run with a simple call. The use of this trait is optional. A `conversion` module is provided for users that decide to implement a custom event loop. + +<p align="center"> +  <img alt="The native target" src="../docs/graphs/native.png" width="80%"> +</p> + +[documentation]: https://docs.rs/iced_glutin +[`iced_native`]: ../native +[`glutin`]: https://github.com/rust-windowing/glutin + +## Installation +Add `iced_glutin` as a dependency in your `Cargo.toml`: + +```toml +iced_glutin = "0.1" +``` + +__Iced moves fast and the `master` branch can contain breaking changes!__ If +you want to learn about a specific release, check out [the release list]. + +[the release list]: https://github.com/hecrj/iced/releases diff --git a/glutin/src/application.rs b/glutin/src/application.rs new file mode 100644 index 00000000..42513feb --- /dev/null +++ b/glutin/src/application.rs @@ -0,0 +1,297 @@ +//! Create interactive, native cross-platform applications. +use crate::{mouse, Error, Executor, Runtime}; + +pub use iced_winit::Application; + +use iced_graphics::window; +use iced_winit::application; +use iced_winit::conversion; +use iced_winit::futures; +use iced_winit::futures::channel::mpsc; +use iced_winit::{Cache, Clipboard, Debug, Proxy, Settings}; + +use glutin::window::Window; +use std::mem::ManuallyDrop; + +/// 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: window::GLCompositor<Renderer = A::Renderer> + 'static, +{ +    use futures::task; +    use futures::Future; +    use glutin::event_loop::EventLoop; +    use glutin::ContextBuilder; + +    let mut debug = Debug::new(); +    debug.startup_started(); + +    let event_loop = EventLoop::with_user_event(); +    let mut 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 subscription = application.subscription(); + +    runtime.spawn(init_command); +    runtime.track(subscription); + +    let context = { +        let builder = settings.window.into_builder( +            &application.title(), +            application.mode(), +            event_loop.primary_monitor(), +        ); + +        let context = ContextBuilder::new() +            .with_vsync(true) +            .with_multisampling(C::sample_count(&compositor_settings) as u16) +            .build_windowed(builder, &event_loop) +            .map_err(|error| { +                use glutin::CreationError; + +                match error { +                    CreationError::Window(error) => { +                        Error::WindowCreationFailed(error) +                    } +                    _ => Error::GraphicsAdapterNotFound, +                } +            })?; + +        #[allow(unsafe_code)] +        unsafe { +            context.make_current().expect("Make OpenGL context current") +        } +    }; + +    #[allow(unsafe_code)] +    let (compositor, renderer) = unsafe { +        C::new(compositor_settings, |address| { +            context.get_proc_address(address) +        })? +    }; + +    let (mut sender, receiver) = mpsc::unbounded(); + +    let mut instance = Box::pin(run_instance::<A, E, C>( +        application, +        compositor, +        renderer, +        context, +        runtime, +        debug, +        receiver, +    )); + +    let mut context = task::Context::from_waker(task::noop_waker_ref()); + +    event_loop.run(move |event, _, control_flow| { +        use glutin::event_loop::ControlFlow; + +        if let ControlFlow::Exit = control_flow { +            return; +        } + +        if let Some(event) = event.to_static() { +            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, +            }; +        } +    }); +} + +async fn run_instance<A, E, C>( +    mut application: A, +    mut compositor: C, +    mut renderer: A::Renderer, +    context: glutin::ContextWrapper<glutin::PossiblyCurrent, Window>, +    mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, +    mut debug: Debug, +    mut receiver: mpsc::UnboundedReceiver<glutin::event::Event<'_, A::Message>>, +) where +    A: Application + 'static, +    E: Executor + 'static, +    C: window::GLCompositor<Renderer = A::Renderer> + 'static, +{ +    use glutin::event; +    use iced_winit::futures::stream::StreamExt; + +    let clipboard = Clipboard::new(context.window()); + +    let mut state = application::State::new(&application, context.window()); +    let mut viewport_version = state.viewport_version(); +    let mut user_interface = +        ManuallyDrop::new(application::build_user_interface( +            &mut application, +            Cache::default(), +            &mut renderer, +            state.logical_size(), +            &mut debug, +        )); + +    let mut primitive = +        user_interface.draw(&mut renderer, state.cursor_position()); +    let mut mouse_interaction = mouse::Interaction::default(); + +    let mut events = Vec::new(); +    let mut messages = Vec::new(); + +    debug.startup_finished(); + +    while let Some(event) = receiver.next().await { +        match event { +            event::Event::MainEventsCleared => { +                if events.is_empty() && messages.is_empty() { +                    continue; +                } + +                debug.event_processing_started(); + +                let statuses = user_interface.update( +                    &events, +                    state.cursor_position(), +                    clipboard.as_ref().map(|c| c as _), +                    &mut renderer, +                    &mut messages, +                ); + +                debug.event_processing_finished(); + +                for event in events.drain(..).zip(statuses.into_iter()) { +                    runtime.broadcast(event); +                } + +                if !messages.is_empty() { +                    let cache = +                        ManuallyDrop::into_inner(user_interface).into_cache(); + +                    // Update application +                    application::update( +                        &mut application, +                        &mut runtime, +                        &mut debug, +                        &mut messages, +                    ); + +                    // Update window +                    state.synchronize(&application, context.window()); + +                    user_interface = +                        ManuallyDrop::new(application::build_user_interface( +                            &mut application, +                            cache, +                            &mut renderer, +                            state.logical_size(), +                            &mut debug, +                        )); +                } + +                debug.draw_started(); +                primitive = +                    user_interface.draw(&mut renderer, state.cursor_position()); +                debug.draw_finished(); + +                context.window().request_redraw(); +            } +            event::Event::UserEvent(message) => { +                messages.push(message); +            } +            event::Event::RedrawRequested(_) => { +                debug.render_started(); +                let current_viewport_version = state.viewport_version(); + +                if viewport_version != current_viewport_version { +                    let physical_size = state.physical_size(); +                    let logical_size = state.logical_size(); + +                    debug.layout_started(); +                    user_interface = ManuallyDrop::new( +                        ManuallyDrop::into_inner(user_interface) +                            .relayout(logical_size, &mut renderer), +                    ); +                    debug.layout_finished(); + +                    debug.draw_started(); +                    primitive = user_interface +                        .draw(&mut renderer, state.cursor_position()); +                    debug.draw_finished(); + +                    context.resize(glutin::dpi::PhysicalSize::new( +                        physical_size.width, +                        physical_size.height, +                    )); + +                    compositor.resize_viewport(physical_size); + +                    viewport_version = current_viewport_version; +                } + +                let new_mouse_interaction = compositor.draw( +                    &mut renderer, +                    state.viewport(), +                    state.background_color(), +                    &primitive, +                    &debug.overlay(), +                ); + +                context.swap_buffers().expect("Swap buffers"); + +                debug.render_finished(); + +                if new_mouse_interaction != mouse_interaction { +                    context.window().set_cursor_icon( +                        conversion::mouse_interaction(new_mouse_interaction), +                    ); + +                    mouse_interaction = new_mouse_interaction; +                } + +                // TODO: Handle animations! +                // Maybe we can use `ControlFlow::WaitUntil` for this. +            } +            event::Event::WindowEvent { +                event: window_event, +                .. +            } => { +                if application::requests_exit(&window_event, state.modifiers()) +                { +                    break; +                } + +                state.update(context.window(), &window_event, &mut debug); + +                if let Some(event) = conversion::window_event( +                    &window_event, +                    state.scale_factor(), +                    state.modifiers(), +                ) { +                    events.push(event); +                } +            } +            _ => {} +        } +    } + +    // Manually drop the user interface +    drop(ManuallyDrop::into_inner(user_interface)); +} diff --git a/glutin/src/lib.rs b/glutin/src/lib.rs new file mode 100644 index 00000000..f2c0102a --- /dev/null +++ b/glutin/src/lib.rs @@ -0,0 +1,25 @@ +//! A windowing shell for [`iced`], on top of [`glutin`]. +//! +//!  +//! +//! [`iced`]: https://github.com/hecrj/iced +//! [`glutin`]: https://github.com/rust-windowing/glutin +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![deny(unused_results)] +#![deny(unsafe_code)] +#![forbid(rust_2018_idioms)] + +pub use glutin; +#[doc(no_inline)] +pub use iced_native::*; + +pub mod application; + +pub use iced_winit::settings; +pub use iced_winit::{Error, Mode}; + +#[doc(no_inline)] +pub use application::Application; +#[doc(no_inline)] +pub use settings::Settings; | 
