diff --git a/winit-core/src/application/mod.rs b/winit-core/src/application/mod.rs index b9fd34bff..abdc2a2af 100644 --- a/winit-core/src/application/mod.rs +++ b/winit-core/src/application/mod.rs @@ -125,7 +125,7 @@ pub trait ApplicationHandler { /// use std::thread; /// use std::time::Duration; /// - /// use winit::event_loop::EventLoop; + /// use winit::event_loop::{EventLoop, EventLoopProvider}; /// use winit_core::application::ApplicationHandler; /// use winit_core::event_loop::ActiveEventLoop; /// diff --git a/winit-core/src/event_loop/mod.rs b/winit-core/src/event_loop/mod.rs index f0b62aeed..38246a85f 100644 --- a/winit-core/src/event_loop/mod.rs +++ b/winit-core/src/event_loop/mod.rs @@ -11,14 +11,104 @@ use std::time::Duration; use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle}; use crate::Instant; +use crate::application::ApplicationHandler; use crate::as_any::AsAny; use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::data_transfer::{DataTransfer, DataTransferId, DataTransferSend, TransferType}; -use crate::error::{NotSupportedError, RequestError}; +use crate::error::{EventLoopError, NotSupportedError, RequestError}; use crate::icon::Icon; use crate::monitor::MonitorHandle; use crate::window::{Theme, Window, WindowAttributes, WindowId}; +/// Common methods to implement for the platform event loop. +pub trait EventLoopProvider: fmt::Debug { + /// Run the event loop with the given application on the calling thread. + /// + /// The `app` is dropped when the event loop is shut down. + /// + /// ## Event loop flow + /// + /// This function internally handles the different parts of a traditional event-handling loop. + /// You can imagine this method as being implemented like this: + /// + /// ```rust,ignore + /// let mut start_cause = StartCause::Init; + /// + /// // Run the event loop. + /// while !event_loop.exiting() { + /// // Wake up. + /// app.new_events(event_loop, start_cause); + /// + /// // Indicate that surfaces can now safely be created. + /// if start_cause == StartCause::Init { + /// app.can_create_surfaces(event_loop); + /// } + /// + /// // Handle proxy wake-up event. + /// if event_loop.proxy_wake_up_set() { + /// event_loop.proxy_wake_up_clear(); + /// app.proxy_wake_up(event_loop); + /// } + /// + /// // Handle actions done by the user / system such as moving the cursor, resizing the + /// // window, changing the window theme, etc. + /// for event in event_loop.events() { + /// match event { + /// window event => app.window_event(event_loop, window_id, event), + /// device event => app.device_event(event_loop, device_id, event), + /// } + /// } + /// + /// // Handle redraws. + /// for window_id in event_loop.pending_redraws() { + /// app.window_event(event_loop, window_id, WindowEvent::RedrawRequested); + /// } + /// + /// // Done handling events, wait until we're woken up again. + /// app.about_to_wait(event_loop); + /// start_cause = event_loop.wait_if_necessary(); + /// } + /// + /// // Finished running, drop application state. + /// drop(app); + /// ``` + /// + /// This is of course a very coarse-grained overview, and leaves out timing details like + /// [`ControlFlow::WaitUntil`] and life-cycle methods like [`ApplicationHandler::resumed`], but + /// it should give you an idea of how things fit together. + /// + /// ## Returns + /// + /// The semantics of this function is defined by the target platform. Consult the implementor + /// docs for details. + fn run_app(self, app: A) -> Result<(), EventLoopError>; + + /// Creates an [`EventLoopProxy`] that can be used to dispatch user events + /// to the main event loop, possibly from another thread. + fn create_proxy(&self) -> EventLoopProxy; + + /// Gets a persistent reference to the underlying platform display. + /// + /// See the [`OwnedDisplayHandle`] type for more information. + fn owned_display_handle(&self) -> OwnedDisplayHandle; + + /// Change if or when [`DeviceEvent`]s are captured. + /// + /// See [`ActiveEventLoop::listen_device_events`] for details. + /// + /// [`DeviceEvent`]: crate::event::DeviceEvent + fn listen_device_events(&self, allowed: DeviceEvents); + + /// Sets the [`ControlFlow`]. + fn set_control_flow(&self, control_flow: ControlFlow); + + /// Create custom cursor. + fn create_custom_cursor( + &self, + custom_cursor: CustomCursorSource, + ) -> Result; +} + pub trait ActiveEventLoop: AsAny + fmt::Debug { /// Creates an [`EventLoopProxy`] that can be used to dispatch user events /// to the main event loop, possibly from another thread. diff --git a/winit-core/src/event_loop/run_on_demand.rs b/winit-core/src/event_loop/run_on_demand.rs index 7c53987a6..120e255ed 100644 --- a/winit-core/src/event_loop/run_on_demand.rs +++ b/winit-core/src/event_loop/run_on_demand.rs @@ -6,11 +6,13 @@ use crate::{ window::Window, }; -/// Additional methods on [`EventLoop`] to return control flow to the caller. +/// Additional methods for [`EventLoopProvider`] to return control flow to the caller. +/// +/// [`EventLoopProvider`]: crate::event_loop::EventLoopProvider pub trait EventLoopExtRunOnDemand { /// Run the application with the event loop on the calling thread. /// - /// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`) + /// Unlike [`EventLoopProvider::run_app`], this function accepts non-`'static` (i.e. non-`move`) /// state and it is possible to return control back to the caller without consuming the /// `EventLoop` (by using [`exit()`]) and so the event loop can be re-run after it has exit. /// @@ -32,8 +34,8 @@ pub trait EventLoopExtRunOnDemand { /// to the caller (specifically this is impossible on iOS and Web). /// - No [`Window`] state can be carried between separate runs of the event loop. /// - /// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you - /// specifically need the ability to re-run a single event loop more than once + /// You are strongly encouraged to use [`EventLoopProvider::run_app`] for portability, unless + /// you specifically need the ability to re-run a single event loop more than once /// /// # Supported Platforms /// - Windows @@ -50,5 +52,6 @@ pub trait EventLoopExtRunOnDemand { /// /// [`exit()`]: ActiveEventLoop::exit() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() + /// [`EventLoopProvider::run_app`]: crate::event_loop::EventLoopProvider::run_app fn run_app_on_demand(&mut self, app: A) -> Result<(), EventLoopError>; } diff --git a/winit/src/changelog/unreleased.md b/winit/src/changelog/unreleased.md index 858652341..52bf89b51 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -61,6 +61,7 @@ changelog entry. matching release of a click that activated a previously inactive window are tagged, so applications can ignore activation clicks for buttons or destructive actions while accepting them for low-risk actions like selection or scrolling. Always `false` on other platforms. +- `winit::event_loop::EventLoopProvider` trait with common event loop methods. ### Changed diff --git a/winit/src/event_loop.rs b/winit/src/event_loop.rs index 57adf6fb6..681de2b1f 100644 --- a/winit/src/event_loop.rs +++ b/winit/src/event_loop.rs @@ -120,58 +120,7 @@ impl EventLoop { /// Run the event loop with the given application on the calling thread. /// - /// The `app` is dropped when the event loop is shut down. - /// - /// ## Event loop flow - /// - /// This function internally handles the different parts of a traditional event-handling loop. - /// You can imagine this method as being implemented like this: - /// - /// ```rust,ignore - /// let mut start_cause = StartCause::Init; - /// - /// // Run the event loop. - /// while !event_loop.exiting() { - /// // Wake up. - /// app.new_events(event_loop, start_cause); - /// - /// // Indicate that surfaces can now safely be created. - /// if start_cause == StartCause::Init { - /// app.can_create_surfaces(event_loop); - /// } - /// - /// // Handle proxy wake-up event. - /// if event_loop.proxy_wake_up_set() { - /// event_loop.proxy_wake_up_clear(); - /// app.proxy_wake_up(event_loop); - /// } - /// - /// // Handle actions done by the user / system such as moving the cursor, resizing the - /// // window, changing the window theme, etc. - /// for event in event_loop.events() { - /// match event { - /// window event => app.window_event(event_loop, window_id, event), - /// device event => app.device_event(event_loop, device_id, event), - /// } - /// } - /// - /// // Handle redraws. - /// for window_id in event_loop.pending_redraws() { - /// app.window_event(event_loop, window_id, WindowEvent::RedrawRequested); - /// } - /// - /// // Done handling events, wait until we're woken up again. - /// app.about_to_wait(event_loop); - /// start_cause = event_loop.wait_if_necessary(); - /// } - /// - /// // Finished running, drop application state. - /// drop(app); - /// ``` - /// - /// This is of course a very coarse-grained overview, and leaves out timing details like - /// [`ControlFlow::WaitUntil`] and life-cycle methods like [`ApplicationHandler::resumed`], but - /// it should give you an idea of how things fit together. + /// For details see [`EventLoopProvider`]. /// /// ## Returns /// @@ -200,6 +149,7 @@ impl EventLoop { /// [`run_app_on_demand`]: crate::event_loop::run_on_demand::EventLoopExtRunOnDemand::run_app_on_demand /// [`run_app_never_return`]: crate::event_loop::never_return::EventLoopExtNeverReturn::run_app_never_return /// [`register_app`]: crate::event_loop::register::EventLoopExtRegister::register_app + /// [`EventLoopProvider`]: winit_core::event_loop::EventLoopProvider /// /// ## Static /// @@ -288,6 +238,35 @@ impl EventLoop { } } +impl EventLoopProvider for EventLoop { + fn run_app(self, app: A) -> Result<(), EventLoopError> { + self.run_app(app) + } + + fn create_proxy(&self) -> EventLoopProxy { + self.create_proxy() + } + + fn owned_display_handle(&self) -> OwnedDisplayHandle { + self.owned_display_handle() + } + + fn listen_device_events(&self, allowed: DeviceEvents) { + self.listen_device_events(allowed); + } + + fn set_control_flow(&self, control_flow: ControlFlow) { + self.set_control_flow(control_flow); + } + + fn create_custom_cursor( + &self, + custom_cursor: CustomCursorSource, + ) -> Result { + self.create_custom_cursor(custom_cursor) + } +} + impl HasDisplayHandle for EventLoop { fn display_handle(&self) -> Result, HandleError> { HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle()) diff --git a/winit/src/lib.rs b/winit/src/lib.rs index e35786470..de1ccb5c4 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -39,7 +39,7 @@ //! ```no_run //! use winit::application::ApplicationHandler; //! use winit::event::WindowEvent; -//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; +//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop, EventLoopProvider}; //! use winit::window::{Window, WindowId, WindowAttributes}; //! //! #[derive(Default)] @@ -260,7 +260,7 @@ //! //! [`EventLoop`]: event_loop::EventLoop //! [`EventLoop::new()`]: event_loop::EventLoop::new -//! [`EventLoop::run_app()`]: event_loop::EventLoop::run_app +//! [`EventLoop::run_app()`]: event_loop::EventLoopProvider::run_app //! [`exit()`]: event_loop::ActiveEventLoop::exit //! [`Window`]: window::Window //! [`WindowId`]: window::WindowId