From da0ba08cffadc9e180b050902aed406662ce0329 Mon Sep 17 00:00:00 2001 From: Simon Heath Date: Tue, 9 Jul 2019 16:29:44 -0400 Subject: [PATCH 1/6] Added some "how" and "why" docs to event handling. Basically I had these questions when I started exploring the new event API's, and as I figured out the answers I put down more info about how everything works. This is not final, and suggestions are welcome -- the code example in the `event` module docs is particularly dubious, but it's how I'm used to thinking abou things so it only made sense to me once I wrote that. Note that my bias is towards using winit for writing games, so that's the sort of things I was interested in. This may not be valid for more general use cases. --- src/event.rs | 40 ++++++++++++++++++++++++++++++++++------ src/lib.rs | 9 +++++++-- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/event.rs b/src/event.rs index d93324f7a8..eac6279e26 100644 --- a/src/event.rs +++ b/src/event.rs @@ -3,6 +3,25 @@ //! 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(...)`] like this: +//! +//! ```rust,no_run +//! let control_flow = &mut ControlFlow::Wait; +//! event_handler(NewEvents(StartCause::Init), ..., control_flow); +//! while *control_flow != ControlFlow::Exit { +//! event_handler(NewEvents(StartCause::Something), ..., control_flow); +//! for e in (window events, user events, device events) { +//! event_handler(e, ..., control_flow); +//! } +//! event_handler(EventsCleared, ..., control_flow); +//! } +//! event_handler(LoopDestroyed, ..., 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; @@ -21,19 +40,27 @@ pub enum Event { 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. + + /// 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. NewEvents(StartCause), - /// Emitted when all events (except for `RedrawRequested`) have been reported. - /// - /// This event is followed by zero or more instances of `RedrawRequested` - /// and, finally, `RedrawEventsCleared`. + + /// Emitted when all of the event loop's events have been processed and control flow is about + /// to be taken away from the program. This event type is useful as a place to put your code that + /// should be run after all events have been handled from the event queue and you want to do + /// stuff (updating state, performing calculations, etc) that happens as the "main body" + /// of an event-handling loop. If your program is doing drawing, it's usually better to do it + /// in response to [`WindowEvent::RedrawRequested`](../event/enum.WindowEvent.html#variant.RedrawRequested). MainEventsCleared, /// The OS or application has requested that a window be redrawn. @@ -48,7 +75,8 @@ pub enum Event { 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, it is guaranteed to be the last event emitted. You generally want to treat + /// this as an "do on quit" event. LoopDestroyed, /// Emitted when the application has been suspended. diff --git a/src/lib.rs b/src/lib.rs index cd99b89fbe..be1fdce80d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,8 +33,12 @@ //! [`EventLoop`] object it was created with. //! //! 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. +//! unless 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. +//! It is no longer possible to use a method like previous versions' `EventLoop::poll_events()` to +//! handle events individually outside of `winit`'s event loop, because this is not possible to do +//! well on web and mobile platforms. For more details on how and why, see [`EventLoopExtDesktop`]. +//! //! //! ```no_run //! use winit::{ @@ -90,6 +94,7 @@ //! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the [`Window`]. //! //! [`EventLoop`]: event_loop::EventLoop +//! [`EventLoopExtDesktop`]: ./platform/desktop/trait.EventLoopExtDesktop.html //! [`EventLoop::new()`]: event_loop::EventLoop::new //! [event_loop_run]: event_loop::EventLoop::run //! [`ControlFlow`]: event_loop::ControlFlow From 4b4bccc1f946120ed6e6735c1d6bff2abe37fe31 Mon Sep 17 00:00:00 2001 From: Simon Heath Date: Tue, 9 Jul 2019 16:32:34 -0400 Subject: [PATCH 2/6] cargo fmt --- src/event.rs | 6 +++--- src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/event.rs b/src/event.rs index eac6279e26..399c482ec2 100644 --- a/src/event.rs +++ b/src/event.rs @@ -40,7 +40,7 @@ pub enum Event { window_id: WindowId, event: WindowEvent, }, - + /// Emitted when the OS sends an event to a device. DeviceEvent { device_id: DeviceId, @@ -49,12 +49,12 @@ pub enum Event { /// 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. 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. NewEvents(StartCause), - + /// Emitted when all of the event loop's events have been processed and control flow is about /// to be taken away from the program. This event type is useful as a place to put your code that /// should be run after all events have been handled from the event queue and you want to do diff --git a/src/lib.rs b/src/lib.rs index be1fdce80d..6e538dee5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,7 +38,7 @@ //! It is no longer possible to use a method like previous versions' `EventLoop::poll_events()` to //! handle events individually outside of `winit`'s event loop, because this is not possible to do //! well on web and mobile platforms. For more details on how and why, see [`EventLoopExtDesktop`]. -//! +//! //! //! ```no_run //! use winit::{ From 2bc50c584fdfa8e4ddf21758616bd8f2403f6199 Mon Sep 17 00:00:00 2001 From: Simon Heath Date: Wed, 10 Jul 2019 19:15:59 -0400 Subject: [PATCH 3/6] Fix minor typos --- src/event.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/event.rs b/src/event.rs index 399c482ec2..580482b937 100644 --- a/src/event.rs +++ b/src/event.rs @@ -3,11 +3,11 @@ //! 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 +//! Some of these events represent different "parts" of a traditional event-handling loop. You could //! approximate the basic ordering loop of [`EventLoop::run(...)`] like this: //! //! ```rust,no_run -//! let control_flow = &mut ControlFlow::Wait; +//! let control_flow = &mut ControlFlow::Poll; //! event_handler(NewEvents(StartCause::Init), ..., control_flow); //! while *control_flow != ControlFlow::Exit { //! event_handler(NewEvents(StartCause::Something), ..., control_flow); From 619adf366ad165a57eaf16982bf233648d37e19a Mon Sep 17 00:00:00 2001 From: Osspial Date: Sat, 4 Jan 2020 16:22:48 -0500 Subject: [PATCH 4/6] Revise event documentation --- src/event.rs | 94 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/src/event.rs b/src/event.rs index 580482b937..e84224f344 100644 --- a/src/event.rs +++ b/src/event.rs @@ -4,19 +4,29 @@ //! 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(...)`] like this: +//! 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); //! -//! ```rust,no_run -//! let control_flow = &mut ControlFlow::Poll; -//! event_handler(NewEvents(StartCause::Init), ..., control_flow); -//! while *control_flow != ControlFlow::Exit { -//! event_handler(NewEvents(StartCause::Something), ..., control_flow); //! for e in (window events, user events, device events) { -//! event_handler(e, ..., control_flow); +//! 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(EventsCleared, ..., control_flow); +//! event_handler(RedrawEventsCleared, ..., &mut control_flow); +//! +//! start_cause = wait_if_necessary(control_flow); //! } -//! event_handler(LoopDestroyed, ..., control_flow); +//! +//! event_handler(LoopDestroyed, ..., &mut control_flow); //! ``` //! //! This leaves out timing details like `ControlFlow::WaitUntil` but hopefully @@ -33,8 +43,18 @@ 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, @@ -50,40 +70,48 @@ pub enum Event { /// 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. 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. - NewEvents(StartCause), + /// Emitted when the application has been suspended. + Suspended, - /// Emitted when all of the event loop's events have been processed and control flow is about - /// to be taken away from the program. This event type is useful as a place to put your code that - /// should be run after all events have been handled from the event queue and you want to do - /// stuff (updating state, performing calculations, etc) that happens as the "main body" - /// of an event-handling loop. If your program is doing drawing, it's usually better to do it - /// in response to [`WindowEvent::RedrawRequested`](../event/enum.WindowEvent.html#variant.RedrawRequested). + /// 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 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. + /// + /// 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). /// - /// Emitted only after `MainEventsCleared`. + /// 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. You generally want to treat - /// this as an "do on quit" event. + /// 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 { From 04cea9ace88bca44d4fd9349aaceb6f16100c66d Mon Sep 17 00:00:00 2001 From: Osspial Date: Sun, 5 Jan 2020 01:45:49 -0500 Subject: [PATCH 5/6] Update lib.rs docs --- src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6e538dee5d..2dd30567b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,9 +35,12 @@ //! You do this by calling [`event_loop.run(...)`][event_loop_run]. This function will run forever //! unless 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. -//! It is no longer possible to use a method like previous versions' `EventLoop::poll_events()` to -//! handle events individually outside of `winit`'s event loop, because this is not possible to do -//! well on web and mobile platforms. For more details on how and why, see [`EventLoopExtDesktop`]. +//! +//! 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. //! //! //! ```no_run @@ -94,7 +97,7 @@ //! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the [`Window`]. //! //! [`EventLoop`]: event_loop::EventLoop -//! [`EventLoopExtDesktop`]: ./platform/desktop/trait.EventLoopExtDesktop.html +//! [`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 From 7bc2f8039d697efecf357f6e51ab70055a9e70d5 Mon Sep 17 00:00:00 2001 From: Osspial Date: Sun, 5 Jan 2020 02:03:36 -0500 Subject: [PATCH 6/6] Update root docs --- src/lib.rs | 76 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2dd30567b5..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,24 @@ //! - 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. -//! -//! Events can be retreived by using an [`EventLoop`]. A [`Window`] will send its events to the -//! [`EventLoop`] object it was created with. -//! -//! You do this by calling [`event_loop.run(...)`][event_loop_run]. This function will run forever -//! unless 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. +//! 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. //! //! 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 @@ -54,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. //! @@ -68,33 +82,20 @@ //! // 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 @@ -103,6 +104,7 @@ //! [`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