winit-core: add EventLoopProvider for common methods

Thus implementing winit main event loop API is standardized.
This commit is contained in:
Kirill Chibisov
2026-07-27 21:18:18 +09:00
parent 93045a5b51
commit d74e5c51d1
6 changed files with 133 additions and 60 deletions

View File

@@ -125,7 +125,7 @@ pub trait ApplicationHandler {
/// use std::thread; /// use std::thread;
/// use std::time::Duration; /// use std::time::Duration;
/// ///
/// use winit::event_loop::EventLoop; /// use winit::event_loop::{EventLoop, EventLoopProvider};
/// use winit_core::application::ApplicationHandler; /// use winit_core::application::ApplicationHandler;
/// use winit_core::event_loop::ActiveEventLoop; /// use winit_core::event_loop::ActiveEventLoop;
/// ///

View File

@@ -11,14 +11,104 @@ use std::time::Duration;
use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle}; use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle};
use crate::Instant; use crate::Instant;
use crate::application::ApplicationHandler;
use crate::as_any::AsAny; use crate::as_any::AsAny;
use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::cursor::{CustomCursor, CustomCursorSource};
use crate::data_transfer::{DataTransfer, DataTransferId, DataTransferSend, TransferType}; 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::icon::Icon;
use crate::monitor::MonitorHandle; use crate::monitor::MonitorHandle;
use crate::window::{Theme, Window, WindowAttributes, WindowId}; 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<A: ApplicationHandler + 'static>(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<CustomCursor, RequestError>;
}
pub trait ActiveEventLoop: AsAny + fmt::Debug { pub trait ActiveEventLoop: AsAny + fmt::Debug {
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
/// to the main event loop, possibly from another thread. /// to the main event loop, possibly from another thread.

View File

@@ -6,11 +6,13 @@ use crate::{
window::Window, 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 { pub trait EventLoopExtRunOnDemand {
/// Run the application with the event loop on the calling thread. /// 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 /// 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. /// `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). /// to the caller (specifically this is impossible on iOS and Web).
/// - No [`Window`] state can be carried between separate runs of the event loop. /// - 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 /// You are strongly encouraged to use [`EventLoopProvider::run_app`] for portability, unless
/// specifically need the ability to re-run a single event loop more than once /// you specifically need the ability to re-run a single event loop more than once
/// ///
/// # Supported Platforms /// # Supported Platforms
/// - Windows /// - Windows
@@ -50,5 +52,6 @@ pub trait EventLoopExtRunOnDemand {
/// ///
/// [`exit()`]: ActiveEventLoop::exit() /// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
/// [`EventLoopProvider::run_app`]: crate::event_loop::EventLoopProvider::run_app
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError>; fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError>;
} }

View File

@@ -61,6 +61,7 @@ changelog entry.
matching release of a click that activated a previously inactive window are tagged, so 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 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. 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 ### Changed

View File

@@ -120,58 +120,7 @@ impl EventLoop {
/// Run the event loop with the given application on the calling thread. /// Run the event loop with the given application on the calling thread.
/// ///
/// The `app` is dropped when the event loop is shut down. /// For details see [`EventLoopProvider`].
///
/// ## 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 /// ## Returns
/// ///
@@ -200,6 +149,7 @@ impl EventLoop {
/// [`run_app_on_demand`]: crate::event_loop::run_on_demand::EventLoopExtRunOnDemand::run_app_on_demand /// [`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 /// [`run_app_never_return`]: crate::event_loop::never_return::EventLoopExtNeverReturn::run_app_never_return
/// [`register_app`]: crate::event_loop::register::EventLoopExtRegister::register_app /// [`register_app`]: crate::event_loop::register::EventLoopExtRegister::register_app
/// [`EventLoopProvider`]: winit_core::event_loop::EventLoopProvider
/// ///
/// ## Static /// ## Static
/// ///
@@ -288,6 +238,35 @@ impl EventLoop {
} }
} }
impl EventLoopProvider for EventLoop {
fn run_app<A: ApplicationHandler + 'static>(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<CustomCursor, RequestError> {
self.create_custom_cursor(custom_cursor)
}
}
impl HasDisplayHandle for EventLoop { impl HasDisplayHandle for EventLoop {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> { fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle()) HasDisplayHandle::display_handle(self.event_loop.window_target().rwh_06_handle())

View File

@@ -39,7 +39,7 @@
//! ```no_run //! ```no_run
//! use winit::application::ApplicationHandler; //! use winit::application::ApplicationHandler;
//! use winit::event::WindowEvent; //! 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}; //! use winit::window::{Window, WindowId, WindowAttributes};
//! //!
//! #[derive(Default)] //! #[derive(Default)]
@@ -260,7 +260,7 @@
//! //!
//! [`EventLoop`]: event_loop::EventLoop //! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoop::new()`]: event_loop::EventLoop::new //! [`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 //! [`exit()`]: event_loop::ActiveEventLoop::exit
//! [`Window`]: window::Window //! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId //! [`WindowId`]: window::WindowId