The current RedrawRequested event is currently hard to understand, which makes it difficult to implement and difficult to use. This issue will outline why I believe that is the case, and how I think we should fix it.
The current problems
RedrawRequested is extremely inconsistent with the rest of our API. This is exhibited in a couple ways:
- It's inconsistent with itself, since it sometimes provides ordering guarantees.
- It's explicitly guaranteed to be the last event emitted if
request_redraw is called, but isn't explicitly guaranteed to be the last event if the OS requested the redraw.
- It's inconsistent with
WindowEvent, since no other window event provides any sort of ordering guarantees.
- It's inconsistent with
EventsCleared, since it can be dispatched after EventsCleared if queued with request_redraw during the EventsCleared handler.
Some of that inconsistency is intentional (point 3), and some isn't (point 1), but the end result is that the API is difficult to explain and difficult to implement. Even beyond the complexity those inconsistencies introduce, the current model can result in duplication of RedrawRequested events when using the recommended event loop structure described in the root docs. Consider the following chain of events, with an application using the linked event loop structure:
- The user resizes the window, resulting in the OS triggering a
RequestRedraw event.
- The event loop dispatches
RequestRedraw between NewEvents and EventsCleared, as part of the standard control flow.
EventsCleared gets dispatched, upon which the user updates the application's state and calls request_redraw.
RequestRedraw gets dispatched immediately after EventsCleared.
The above situation has a couple problems: the first RedrawRequested is dispatched before the application has a chance to update its internal state, and the application goes through the expensive process of rendering a frame twice in a single iteration of the event loop. Ideally, our API wouldn't exhibit either issue, allowing the user to update their application state before all redraws occur and allowing Winit to unify redraw requests from the user and OS into a single event.
Proposed changes
- Rename
EventsCleared to MainEventsCleared
- Remove
RedrawRequested from WindowEvent.
- Add
RedrawRequested(WindowId) to Event.
- Guarantee that
RedrawRequested get dispatched between MainEventsCleared and RedrawEventsCleared.
- Add
RedrawEventsCleared to Event.
The rough event loop would end up behaving something like this (adapted from @icefoxen's example in #1032):
let event_loop_windows = /* event loop windows */;
let mut control_flow = ControlFlow::Poll;
event_handler(NewEvents(StartCause::Init), &event_loop_windows, &mut control_flow);
while control_flow != ControlFlow::Exit {
event_handler(NewEvents(/* get start cause */), &event_loop_windows, &mut control_flow);
for e in (window events, user events, device events) {
event_handler(e, &event_loop_windows, &mut control_flow);
}
event_handler(MainEventsCleared, &event_loop_windows, &mut control_flow);
for window_id in (redraw_windows) {
event_handler(RedrawRequested(window_id));
}
event_handler(RedrawEventsCleared, &event_loop_windows, &mut control_flow);
/* wait or poll, as the control_flow dictates */
}
event_handler(LoopDestroyed, &event_loop_windows, &mut control_flow);
This results in numerous usability improvements over the current model:
RedrawRequested's status as a special event is made more clear, as it's structurally distinct from standard window events.
RedrawRequested is now guaranteed to be dispatched after event processing and application update code has been run, fitting it more naturally into the application update/render loop.
- Winit can now unify all OS-requested and user-requested redraw events, avoiding redundant redrawing.
Feedback is encouraged on the above designs, especially WRT naming - I'm not entirely happy with the names I've provided, and mainly included them because I can't think of anything better right now. There may be a better solution to the current API's problems, but I'm presently unable to think of one and we should certainly address them before 0.20's full release.
The current
RedrawRequestedevent is currently hard to understand, which makes it difficult to implement and difficult to use. This issue will outline why I believe that is the case, and how I think we should fix it.The current problems
RedrawRequestedis extremely inconsistent with the rest of our API. This is exhibited in a couple ways:request_redrawis called, but isn't explicitly guaranteed to be the last event if the OS requested the redraw.WindowEvent, since no other window event provides any sort of ordering guarantees.EventsCleared, since it can be dispatched afterEventsClearedif queued withrequest_redrawduring theEventsClearedhandler.Some of that inconsistency is intentional (point 3), and some isn't (point 1), but the end result is that the API is difficult to explain and difficult to implement. Even beyond the complexity those inconsistencies introduce, the current model can result in duplication of
RedrawRequestedevents when using the recommended event loop structure described in the root docs. Consider the following chain of events, with an application using the linked event loop structure:RequestRedrawevent.RequestRedrawbetweenNewEventsandEventsCleared, as part of the standard control flow.EventsClearedgets dispatched, upon which the user updates the application's state and callsrequest_redraw.RequestRedrawgets dispatched immediately afterEventsCleared.The above situation has a couple problems: the first
RedrawRequestedis dispatched before the application has a chance to update its internal state, and the application goes through the expensive process of rendering a frame twice in a single iteration of the event loop. Ideally, our API wouldn't exhibit either issue, allowing the user to update their application state before all redraws occur and allowing Winit to unify redraw requests from the user and OS into a single event.Proposed changes
EventsClearedtoMainEventsClearedRedrawRequestedfromWindowEvent.RedrawRequested(WindowId)toEvent.RedrawRequestedget dispatched betweenMainEventsClearedandRedrawEventsCleared.RedrawEventsClearedtoEvent.The rough event loop would end up behaving something like this (adapted from @icefoxen's example in #1032):
This results in numerous usability improvements over the current model:
RedrawRequested's status as a special event is made more clear, as it's structurally distinct from standard window events.RedrawRequestedis now guaranteed to be dispatched after event processing and application update code has been run, fitting it more naturally into the application update/render loop.Feedback is encouraged on the above designs, especially WRT naming - I'm not entirely happy with the names I've provided, and mainly included them because I can't think of anything better right now. There may be a better solution to the current API's problems, but I'm presently unable to think of one and we should certainly address them before 0.20's full release.