mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Use the system's mechanisms for pumping events, namely `nextEventMatchingMask:untilDate:inMode:dequeue:`. This still has a few problems as detailed in the documentation for `pump_app_events` (which is why we switched away from it all the way back in Winit v0.20), but it doesn't have nearly as many problems as the current implementation does.
123 lines
5.3 KiB
Rust
123 lines
5.3 KiB
Rust
use std::time::Duration;
|
|
|
|
use crate::application::ApplicationHandler;
|
|
use crate::event_loop::EventLoop;
|
|
|
|
/// Additional methods on [`EventLoop`] for pumping events within an external event loop
|
|
pub trait EventLoopExtPumpEvents {
|
|
/// Pump the `EventLoop` to check for and dispatch pending events.
|
|
///
|
|
/// This API is designed to enable applications to integrate Winit into an
|
|
/// external event loop, for platforms that can support this.
|
|
///
|
|
/// The given `timeout` limits how long it may block waiting for new events.
|
|
///
|
|
/// Passing a `timeout` of `Some(Duration::ZERO)` would ensure your external
|
|
/// event loop is never blocked but you would likely need to consider how
|
|
/// to throttle your own external loop.
|
|
///
|
|
/// Passing a `timeout` of `None` means that it may wait indefinitely for new
|
|
/// events before returning control back to the external loop.
|
|
///
|
|
/// **Note:** This is not a portable API, and its usage involves a number of
|
|
/// caveats and trade offs that should be considered before using this API!
|
|
///
|
|
/// You almost certainly shouldn't use this API, unless you absolutely know it's
|
|
/// the only practical option you have.
|
|
///
|
|
/// ## Synchronous events
|
|
///
|
|
/// Some events _must_ only be handled synchronously via the closure that
|
|
/// is passed to Winit so that the handler will also be synchronized with
|
|
/// the window system and operating system.
|
|
///
|
|
/// This is because some events are driven by a window system callback
|
|
/// where the window systems expects the application to have handled the
|
|
/// event before returning.
|
|
///
|
|
/// **These events can not be buffered and handled outside of the closure
|
|
/// passed to Winit.**
|
|
///
|
|
/// As a general rule it is not recommended to ever buffer events to handle
|
|
/// them outside of the closure passed to Winit since it's difficult to
|
|
/// provide guarantees about which events are safe to buffer across all
|
|
/// operating systems.
|
|
///
|
|
/// Notable events that will certainly create portability problems if
|
|
/// buffered and handled outside of Winit include:
|
|
/// - `RedrawRequested` events, used to schedule rendering.
|
|
///
|
|
/// macOS for example uses a `drawRect` callback to drive rendering
|
|
/// within applications and expects rendering to be finished before
|
|
/// the `drawRect` callback returns.
|
|
///
|
|
/// For portability it's strongly recommended that applications should
|
|
/// keep their rendering inside the closure provided to Winit.
|
|
/// - Any lifecycle events, such as `Suspended` / `Resumed`.
|
|
///
|
|
/// The handling of these events needs to be synchronized with the
|
|
/// operating system and it would never be appropriate to buffer a
|
|
/// notification that your application has been suspended or resumed and
|
|
/// then handled that later since there would always be a chance that
|
|
/// other lifecycle events occur while the event is buffered.
|
|
///
|
|
/// ## Supported Platforms
|
|
///
|
|
/// - Windows
|
|
/// - Linux
|
|
/// - MacOS
|
|
/// - Android
|
|
///
|
|
/// ## Unsupported Platforms
|
|
///
|
|
/// - **Web:** This API is fundamentally incompatible with the event-based way in which Web
|
|
/// browsers work because it's not possible to have a long-running external loop that would
|
|
/// block the browser and there is nothing that can be polled to ask for new new events.
|
|
/// Events are delivered via callbacks based on an event loop that is internal to the browser
|
|
/// itself.
|
|
/// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS so
|
|
/// there's no way to support the same approach to polling as on MacOS.
|
|
///
|
|
/// ## Platform-specific
|
|
///
|
|
/// - **Windows**: The implementation will use `PeekMessage` when checking for window messages
|
|
/// to avoid blocking your external event loop.
|
|
///
|
|
/// - **MacOS**: Certain actions like resizing the window will enter a "modal" state, where
|
|
/// `pump_app_events` will process events internally, and block until the resize is over.
|
|
///
|
|
/// Thus, if you render or run your game code outside of `ApplicationHandler`, your
|
|
/// application will freeze while the window resizes. The recommended approach is to render
|
|
/// inside [`WindowEvent::RedrawRequested`] instead.
|
|
///
|
|
/// Furthermore, when pumping events the `NSApplication` is still considered stopped to
|
|
/// crates like `rfd` that inspect [`-[NSApplication isRunning]`][isrunning].
|
|
///
|
|
/// [`WindowEvent::RedrawRequested`]: crate::event::WindowEvent::RedrawRequested
|
|
/// [isrunning]: https://developer.apple.com/documentation/appkit/nsapplication/isrunning?language=objc
|
|
fn pump_app_events<A: ApplicationHandler>(
|
|
&mut self,
|
|
timeout: Option<Duration>,
|
|
app: A,
|
|
) -> PumpStatus;
|
|
}
|
|
|
|
impl EventLoopExtPumpEvents for EventLoop {
|
|
fn pump_app_events<A: ApplicationHandler>(
|
|
&mut self,
|
|
timeout: Option<Duration>,
|
|
app: A,
|
|
) -> PumpStatus {
|
|
self.event_loop.pump_app_events(timeout, app)
|
|
}
|
|
}
|
|
|
|
/// The return status for `pump_events`
|
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
pub enum PumpStatus {
|
|
/// Continue running external loop.
|
|
Continue,
|
|
/// Exit external loop.
|
|
Exit(i32),
|
|
}
|