-
Notifications
You must be signed in to change notification settings - Fork 921
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch to a futures-compatible API #126
Conversation
Note: do not forget to test the Android build before merging. |
//! if none is available. Depending on which kind of program you're writing, you usually choose | ||
//! one or the other. | ||
//! Once a window has been created, it will *generate events*. For example whenever the user moves | ||
//! the window, resizes the window, moves the mouse, etc. an event is generated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a thought, would it be the proper place to add a note that on some platforms, no events will be generated before something is drawn on the window (this is the case of wayland)? This can totally be done in a followup PR btw, it's just that reading this here made me think of it.
I like this new API. 👍 As it matches pretty well the wayland event flow, it'll allow me to remove a large part of the current runtime overhead of the wayland backend. Just, to make sure I got it correctly. From the point of view of the backends, doing the proper migration means implementing an impl EventLoop {
pub fn new() -> EventLoop { ... }
pub fn poll_events<F>(&self, cb: F) where F: FnMut(::Event) { ... }
pub fn run_forever<F>(&self, cb: F) where F: FnMut(::Event) { ... }
} and update the window internals to dispatch the events through the event loop. Is it correct? Secondary question: is it supposed to be valid for an user to have more than one |
Yes, the backends are supposed to be exactly like the public API.
Yes, is it a problem? |
Not at all, but this has consequences on the implementation, so I wanted to be sure we are on the same page. |
It seems weird to me to provide a pub fn wait_event<F>(&self, cb: F) where F: FnOnce(::Event) { ... } ..or just return the pub fn wait_event(&self) -> Event { ... } Does it have to include the loop? |
@Darkstalker There's the problem of OS X which interrupts your main thread while your window is being resized, which means that the only way to do something is through the callback handler. |
Let's do that final comment period thing for this design. |
@tomaka thanks for inviting the FCP. Is the expectation with this implementation that drawing should happen on a secondary thread? |
@jwilm No. Today you usually do something like this: loop {
for ev in window.poll_events() {
match ev { ... }
}
draw();
} With the new API it would be something like this: loop {
events_loop.poll_events(|ev| {
match ev { ... }
});
draw();
} |
@tomaka oops, I missed the My other concern regards removal of the |
Yes, |
@tomaka perfect. This all sounds good! |
@@ -60,7 +68,6 @@ pub enum Event { | |||
/// The parameter is true if app was suspended, and false if it has been resumed. | |||
Suspended(bool), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we move this variant to the Event
enum? Also perhaps the Awakened
variant (although I noticed it looks like you're planning on removing this).
I'm wondering if also the ReceivedCharacter
and KeyboardInput
variants should be moved there also? Or perhaps they should stay in the WindowEvent
enum and only be delivered to the currently focused window?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, the problem with Suspended
and Awakened
is that I need to change the backends' code if I remove them.
As for the keyboard events, the windows compositor usually sends the event only to the currently focused window, so sending it to a specific window is how it's designed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the problem with Suspended and Awakened is that I need to change the backends' code if I remove them.
I guess it's probably OK to leave for a follow-up PR then. You could do it on another branch and wait for contributors from each backend to do PRs to that branch if you can't do each backend yourself.
As for the keyboard events, the windows compositor usually sends the event only to the currently focused window, so sending it to a specific window is how it's designed.
Now that you mention it, this also seems to be the way macOS windows work too.
src/events.rs
Outdated
@@ -2,6 +2,17 @@ use std::path::PathBuf; | |||
|
|||
#[derive(Clone, Debug)] | |||
pub enum Event { | |||
WindowEvent { | |||
window_id: usize, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this should be it's own Id
type defined in the window
module? That way we could also document it, specifying that every window will have a unique identifier associated with it for the duration of its lifetime? Maybe this is fine though, I'm not sure.
@@ -2,6 +2,17 @@ use std::path::PathBuf; | |||
|
|||
#[derive(Clone, Debug)] | |||
pub enum Event { | |||
WindowEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variant could probably just be Window
as we already know that it is an Event
via the enum name, e.g. Event::Window
. Otherwise we end up repeating "Event" twice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A window is not an event though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already know that it is an Event
though, it is the name of the type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a big deal, just a suggestion.
It seems like the |
There's no |
There's a |
@tomaka should it be possible for a user to call events_loop.run_forever(|event| {
events_loop.poll_events(|event| {
// ..etc
})
}) The API as it is allows it, however this has some implications for the macOS implementation, as the macOS implementation must be able to share the user-given callback to each of the window delegate's callbacks (for resize, etc). I think I can allow this by keeping the user-given callbacks in a stack (edit: actually I think I can just use the call stack), but just thought I'd check that allowing this is intentional first. Edit: Also, if this kind of nesting is to be allowed, should the |
A minor point: the following might be more ergonomic and suitable method names:
I think these are more concise while still making it obvious that we are polling or waiting for events. I think using
Sorry for the late bike-shedding! Only just getting time to properly look over all this over the past couple days. |
Maybe it's best to simply panic if the user does so.
The reason why I don't really like |
About the nesting, my wayland lib has a similar pattern, but the methods require With the wayland backend, nesting |
If it required |
Oh, right., forgot about that. I've solved this issue in |
Well, the other reason is that ideally the events loop should be sharable amongst threads (even if that means that a mutex is used internally) unless there's a good reason not to. I suggest that the code just panics if |
Ok, let's merge this. The next steps are to write a PR that panics if we call a callback from within a callback, and to update the backends. |
winuser::ShowWindow will show the window and make set_visible(false) obsolete.
Implements #20
What does this do?
Instead of polling and waiting for events on the window directly, you first have to register your window on an events loop.
This events loop can then be polled (with
poll_events()
) or waited upon (withrun_forever()
), just like in the current API.See the new
window
example for an example usage.There are several advantages to this approach:
Event
enum has been renamed toWindowEvent
, and a new enum namedEvent
contains all the possible "global" events.WindowProxy
system has been removed.Event
struct if necessary (eg. to pass strings as&str
instead ofString
).Implementation strategy
In order to make the transition smoother and avoid having to rewrite all the implementation, I created a macro named
gen_api_transition!
which creates types that expose the new API, and internally uses the old API.For the moment each backend invokes this macro in order to make itself compatible with the new API.
In the future, each backend should be rewritten to directly implement the new API without going through this macro. Once everything has been transferred, the macro should be removed.
The macro doesn't attempt to register a resize callback, because it was too complex. However it should be easy to pass callback events to the events loop if we modify the OS X backend.
A necessary change was that the frontend now uses a type named
Window2
instead ofWindow
. This should also be reverted once possible.Close #20