diff --git a/src/event.rs b/src/event.rs index d93324f7a8..e84224f344 100644 --- a/src/event.rs +++ b/src/event.rs @@ -3,6 +3,35 @@ //! These are sent to the closure given to [`EventLoop::run(...)`][event_loop_run], where they get //! processed and used to modify the program state. For more details, see the root-level documentation. //! +//! Some of these events represent different "parts" of a traditional event-handling loop. You could +//! approximate the basic ordering loop of [`EventLoop::run(...)`][event_loop_run] like this: +//! +//! ```rust,ignore +//! let mut control_flow = ControlFlow::Poll; +//! let mut start_cause = StartCause::Init; +//! +//! while control_flow != ControlFlow::Exit { +//! event_handler(NewEvents(start_cause), ..., &mut control_flow); +//! +//! for e in (window events, user events, device events) { +//! event_handler(e, ..., &mut control_flow); +//! } +//! event_handler(MainEventsCleared, ..., &mut control_flow); +//! +//! for w in (redraw windows) { +//! event_handler(RedrawRequested(w), ..., &mut control_flow); +//! } +//! event_handler(RedrawEventsCleared, ..., &mut control_flow); +//! +//! start_cause = wait_if_necessary(control_flow); +//! } +//! +//! event_handler(LoopDestroyed, ..., &mut control_flow); +//! ``` +//! +//! This leaves out timing details like `ControlFlow::WaitUntil` but hopefully +//! describes what happens in what order. +//! //! [event_loop_run]: crate::event_loop::EventLoop::run use instant::Instant; use std::path::PathBuf; @@ -14,48 +43,75 @@ use crate::{ }; /// Describes a generic event. +/// +/// See the module-level docs for more information on the event loop manages each event. #[derive(Clone, Debug, PartialEq)] pub enum Event { + /// Emitted when new events arrive from the OS to be processed. + /// + /// This event type is useful as a place to put code that should be done before you start + /// processing events, such as updating frame timing information for benchmarking or checking + /// the [`StartCause`][crate::event::StartCause] to see if a timer set by + /// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed. + NewEvents(StartCause), + /// Emitted when the OS sends an event to a winit window. WindowEvent { window_id: WindowId, event: WindowEvent, }, + /// Emitted when the OS sends an event to a device. DeviceEvent { device_id: DeviceId, event: DeviceEvent, }, + /// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event) UserEvent(T), - /// Emitted when new events arrive from the OS to be processed. - NewEvents(StartCause), - /// Emitted when all events (except for `RedrawRequested`) have been reported. + + /// Emitted when the application has been suspended. + Suspended, + + /// Emitted when the application has been resumed. + Resumed, + + /// Emitted when all of the event loop's input events have been processed and redraw processing + /// is about to begin. /// - /// This event is followed by zero or more instances of `RedrawRequested` - /// and, finally, `RedrawEventsCleared`. + /// This event is useful as a place to put your code that should be run after all + /// state-changing events have been handled and you want to do stuff (updating state, performing + /// calculations, etc) that happens as the "main body" of your event loop. If your program draws + /// graphics, it's usually better to do it in response to + /// [`Event::RedrawRequested`](crate::event::Event::RedrawRequested), which gets emitted + /// immediately after this event. MainEventsCleared, - /// The OS or application has requested that a window be redrawn. + /// Emitted after `MainEventsCleared` when a window should be redrawn. /// - /// Emitted only after `MainEventsCleared`. + /// This gets triggered in two scenarios: + /// - The OS has performed an operation that's invalidated the window's contents (such as + /// resizing the window). + /// - The application has explicitly requested a redraw via + /// [`Window::request_redraw`](crate::window::Window::request_redraw). + /// + /// During each iteration of the event loop, Winit will aggregate duplicate redraw requests + /// into a single event, to help avoid duplicating rendering work. RedrawRequested(WindowId), - /// Emitted after any `RedrawRequested` events. + /// Emitted after all `RedrawRequested` events have been processed and control flow is about to + /// be taken away from the program. If there are no `RedrawRequested` events, it is emitted + /// immediately after `MainEventsCleared`. /// - /// If there are no `RedrawRequested` events, it is reported immediately after - /// `MainEventsCleared`. + /// This event is useful for doing any cleanup or bookkeeping work after all the rendering + /// tasks have been completed. RedrawEventsCleared, - /// Emitted when the event loop is being shut down. This is irreversable - if this event is - /// emitted, it is guaranteed to be the last event emitted. + /// Emitted when the event loop is being shut down. + /// + /// This is irreversable - if this event is emitted, it is guaranteed to be the last event that + /// gets emitted. You generally want to treat this as an "do on quit" event. LoopDestroyed, - - /// Emitted when the application has been suspended. - Suspended, - - /// Emitted when the application has been resumed. - Resumed, } impl Event { diff --git a/src/lib.rs b/src/lib.rs index cd99b89fbe..9a9fc02c9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ -//! Winit allows you to build a window on as many platforms as possible. +//! Winit is a cross-platform window creation and event loop management library. //! -//! # Building a window +//! # Building windows //! //! Before you can build a [`Window`], you first need to build an [`EventLoop`]. This is done with the //! [`EventLoop::new()`] function. @@ -15,26 +15,31 @@ //! - Calling [`Window::new(&event_loop)`][window_new]. //! - Calling [`let builder = WindowBuilder::new()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build]. //! -//! The first way is the simplest way and will give you default values for everything. -//! -//! The second way allows you to customize the way your [`Window`] will look and behave by modifying -//! the fields of the [`WindowBuilder`] object before you create the [`Window`]. +//! The first method is the simplest, and will give you default values for everything. The second +//! method allows you to customize the way your [`Window`] will look and behave by modifying the +//! fields of the [`WindowBuilder`] object before you create the [`Window`]. //! //! # Event handling //! //! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can -//! generate a [`WindowEvent`] when certain things happen, like whenever the user moves their mouse -//! or presses a key inside the [`Window`]. Devices can generate a [`DeviceEvent`] directly as well, -//! which contains unfiltered event data that isn't specific to a certain window. Some user -//! activity, like mouse movement, can generate both a [`WindowEvent`] *and* a [`DeviceEvent`]. You -//! can also create and handle your own custom [`UserEvent`]s, if desired. +//! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the +//! window or a key getting pressed while the window is focused. Devices can generate +//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window. +//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a +//! [`DeviceEvent`]. You can also create and handle your own custom [`UserEvent`]s, if desired. +//! +//! You can retreive events by calling [`EventLoop::run`][event_loop_run]. This function will +//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and +//! will run until the `control_flow` argument given to the closure is set to +//! [`ControlFlow`]`::`[`Exit`], at which point [`Event`]`::`[`LoopDestroyed`] is emitted and the +//! entire program terminates. //! -//! Events can be retreived by using an [`EventLoop`]. A [`Window`] will send its events to the -//! [`EventLoop`] object it was created with. +//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator`-based event loop +//! model, since that can't be implemented properly on web and mobile platforms and works poorly on +//! most desktop platforms. However, this model can be re-implemented to an extent on desktops with +//! [`EventLoopExtDesktop::run_return`]. See that method's documentation for more reasons about why +//! it's discouraged, beyond mobile/web compatibility reasons. //! -//! You do this by calling [`event_loop.run(...)`][event_loop_run]. This function will run forever -//! unless `control_flow` is set to [`ControlFlow`]`::`[`Exit`], at which point [`Event`]`::`[`LoopDestroyed`] -//! is emitted and the entire program terminates. //! //! ```no_run //! use winit::{ @@ -47,7 +52,23 @@ //! let window = WindowBuilder::new().build(&event_loop).unwrap(); //! //! event_loop.run(move |event, _, control_flow| { +//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't +//! // dispatched any events. This is ideal for games and similar applications. +//! *control_flow = ControlFlow::Poll; +//! +//! // ControlFlow::Wait pauses the event loop if no events are available to process. +//! // This is ideal for non-game applications that only update in response to user +//! // input, and uses significantly less power/CPU time than ControlFlow::Poll. +//! *control_flow = ControlFlow::Wait; +//! //! match event { +//! Event::WindowEvent { +//! event: WindowEvent::CloseRequested, +//! .. +//! } => { +//! println!("The close button was pressed; stopping"); +//! *control_flow = ControlFlow::Exit +//! }, //! Event::MainEventsCleared => { //! // Application update code. //! @@ -61,40 +82,29 @@ //! // rendering in here allows the program to gracefully handle redraws requested //! // by the OS. //! }, -//! Event::WindowEvent { -//! event: WindowEvent::CloseRequested, -//! .. -//! } => { -//! println!("The close button was pressed; stopping"); -//! *control_flow = ControlFlow::Exit -//! }, -//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't -//! // dispatched any events. This is ideal for games and similar applications. -//! _ => *control_flow = ControlFlow::Poll, -//! // ControlFlow::Wait pauses the event loop if no events are available to process. -//! // This is ideal for non-game applications that only update in response to user -//! // input, and uses significantly less power/CPU time than ControlFlow::Poll. -//! // _ => *control_flow = ControlFlow::Wait, +//! _ => () //! } //! }); //! ``` //! -//! If you use multiple [`Window`]s, [`Event`]`::`[`WindowEvent`] has a member named `window_id`. You can -//! compare it with the value returned by the [`id()`][window_id_fn] method of [`Window`] in order to know which -//! [`Window`] has received the event. +//! [`Event`]`::`[`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be +//! compared to the value returned by [`Window::id()`][window_id_fn] to determine which [`Window`] +//! dispatched the event. //! //! # Drawing on the window //! -//! Winit doesn't provide any function that allows drawing on a [`Window`]. However it allows you to +//! Winit doesn't directly provide any methods for drawing on a [`Window`]. However it allows you to //! retrieve the raw handle of the window (see the [`platform`] module), which in turn allows you -//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the [`Window`]. +//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics. //! //! [`EventLoop`]: event_loop::EventLoop +//! [`EventLoopExtDesktop::run_return`]: ./platform/desktop/trait.EventLoopExtDesktop.html#tymethod.run_return //! [`EventLoop::new()`]: event_loop::EventLoop::new //! [event_loop_run]: event_loop::EventLoop::run //! [`ControlFlow`]: event_loop::ControlFlow //! [`Exit`]: event_loop::ControlFlow::Exit //! [`Window`]: window::Window +//! [`WindowId`]: window::WindowId //! [`WindowBuilder`]: window::WindowBuilder //! [window_new]: window::Window::new //! [window_builder_new]: window::WindowBuilder::new