- Depricate the
poll_events
function, in favor ofrun_forever
with the expandedControlFlow
enum. - Change the definition of
ControlFlow
to the following:pub enum ControlFlow { /// Equivalent to the current `Continue`. Suspends the thread until another OS event /// arrives. Wait, /// Suspends the thread until either another event arrives, or the timeout expires. WaitTimeout(Duration), /// Replaces the `poll_events` method's functionality. After all the OS's events have /// been processed, the callback is repeatedly called with `Event::Poll`, until the /// next event arrives. Poll, /// Breaks out of the event loop. Break }
- Add the following variants to the
Event
enum:/// Sent if the time specified by `ControlFlow::WaitTimeout` has been elapsed. Contains the /// moment the timeout was requested, and the requested duration of the timeout. TimeoutExpired { start: Instant, duration: Duration }, /// Sent if the OS has new events to send to the window, after a wait was requested. Contains /// the moment the wait was requested, and if a wait timout was requested, its duration. WaitCancelled { start: Instant, requested_duration: Option<Duration> }, /// Sent after `ControlFlow::Poll` was returned by the callback, and all the OS events have /// been exhausted. Poll
- Change the event loop's callback definition, so that it takes a new type:
EventsLoopWindows
- Change
Window::new()
andWindowBuilder::build()
to take this new type. - Implement
Deref<Target=EventsLoopWindows>
forEventsLoop
. - Motivation: Currently, a programmer can't create new windows while inside of
run_forever
, as the function mutably borrowsEventsLoop
. Adding this new type would allow them to do that.
- Change
- Increase the importance of
WindowEvent::Refresh
:- Make handling this event the officially recommended method of redrawing a window.
- Add the following function to
Window
:
/// Makes the events loop send a `WindowEvent::Refresh` event, after all OS events have been /// processed by the event loop (`WindowEvent`, `DeviceEvent`, `Awakened`, and `Suspended`), but before any /// winit events have been processed (`TimeoutExpred`, `WaitCancelled`, and `Poll`). fn queue_refresh(&self) {...}
-
Motivations:
- Integrates better with OS-generated redraw events, i.e. when a window is resized (on Windows, this is through the
WM_PAINT
message). By using this, we can handle window redrawing entirely within the OS callback, which is where the OS expects this to happen. This should improve the smoothness of window resizes, and will hopefully fix the current issue where resizing the window leaves some sort of blank space at the edges of the window, before the program has a chance to draw content to fill it. - Makes it much easier for the program to redraw only when necessary, especially when waiting for events. Currently, if a program uses
run_forever
and wants to only redraws when user input is received, the simplest method is to redraw after every event is received, like so:
events_loop.run_forever(|event, _| { match event { Event::WindowEvent{..} => { /* Update program state */ }, _ => () } /* Redraw the window */ ControlFlow::Continue });
This is the wrong approach: it isn't uncommon for the OS to send multiple events in the duration of a single frame, and redrawing after every event wastes time rendering an image the program's user will never see, and can slow down the program. Right now, the only way I can see to do this properly is to do the following:
loop { let update_state = |event| {/* Updates program state */}; // Wait for the first OS event to be received, and then exit the event loop. events_loop.run_forever(|event| {update_state(event); ControlFlow::Break}); // Handle the rest of the frame's events. events_loop.poll_events(update_state); /* Redraw the window */ }
I don't think that's the kind of design we want to be encouraging, and neither the issue nor the fix is obvious to a programmer using winit. If
run_forever
is being used, the program should spend pretty much its entire lifetime within the loop, and right now that's discouraged. Using this API would let us write the event loop as the following:events_loop.run_forever(|event, _| { match event { Event::WindowEvent{..} => { /* Update program state */ // Tells winit to queue a window refresh. Importantly, the refresh doesn't happen right // when this function is called, letting winit delay drawing until after we've processed // all the user input events and the program's state has stopped changing at a rate faster // than the user can see. window.queue_refresh(); }, Event::Refresh => { /* Redraw the window */ }, _ => () } ControlFlow::Wait });
Which is both cleaner and a more direct way to have the program behave the right way.
- Integrates better with OS-generated redraw events, i.e. when a window is resized (on Windows, this is through the
Created
August 24, 2018 01:27
-
-
Save Osspial/53efa87797899872650f6bece14736c0 to your computer and use it in GitHub Desktop.
Event Loop 2.0 Original Design
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment