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::time::Duration;
///
/// use winit::event_loop::EventLoop;
/// use winit::event_loop::{EventLoop, EventLoopProvider};
/// use winit_core::application::ApplicationHandler;
/// use winit_core::event_loop::ActiveEventLoop;
///

View File

@@ -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<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 {
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
/// to the main event loop, possibly from another thread.

View File

@@ -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<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError>;
}