-
Notifications
You must be signed in to change notification settings - Fork 904
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
RFC: Switch to a futures-compatible API #20
Comments
I guess I'll ping some people to get some opinions: @ozkriff @mitchmindtree @kvark @fkaa @vberger @glennw @larsbergstrom |
How would this allow winit to not spawn a background thread on Windows? Rescaling a window causes the callback thread to be blocked until the end of the rescale, a behavior which is currently hidden by the background thread, and as far as I know there isn't any way to stop that behavior. Moving it onto the main thread would move the blocking there. |
There are two main trade-offs that come to mind with this approach:
I'm curious why this is a benefit? Is it because you're unsure how to both make the window handle available to the user while also using it to run the events loop? Another way (using glutin's current style) might be to pass the window to the method that yields the next event, something like: loop {
let poll = events_loop.poll();
while let Some(event) = poll.next(&mut window) {
// Still have access to the window handle
}
} I'm not familiar enough with the issues related to the other benefits you mentioned to be able to comment on whether they are worth the trade-offs I mentioned above. |
@Osspial Are you sure about that? On Win32 you're supposed to poll and dispatch the messages manually with @mitchmindtree Don't forget that the closure can borrow its environment. window.set_callback(|ev| {
match ev {
Event::Closed => events_loop.stop(),
_ => ()
}
});
events_loop.run_until_stopped(); Or, this: let mut events = RefCell::new(Vec::new());
window.set_callback(|ev| events.borrow_mut().push(ev));
loop {
events_loop.run_once();
for event in events.borrow_mut().drain(..) {
...
}
} It's true that if you want to isolate the window in a struct, then you will need a
However I think this use case doesn't happen often.
The benefit of decoupling the window from the events loop is that on MacOS we can run the events loop on the main thread and the window in another thread, and that with X11 we can do everything in a single-threaded fashion by creating one X connection per EventLoop, which will likely lead to less bugs in xlib. |
@tomaka Yeah. I was able to get winit working without multithreading on Windows with the current API, and everything worked fine except for rescales, which blocked execution. Apparently, Windows enters an internal loop during rescales that does end up calling the user-defined window procedure, but doesn't return from |
One interesting problem is about leak safety. Future-rs's tasks system is very complex and avoids this problem by running the callback only if we call a method on either the future itself or the task that holds the future. |
Looking at this issue again and looking over the benefits you mention, I think they're easily worth the issues I mentioned, and the workarounds you shared look really reasonable anyways. 👍 from me. |
@tomaka would it make sense to actually make this use |
@tschneidereit I'd be happy to advise more specifically if you decide to go this direction! |
@aturon thanks! I guess the main question is whether it's prudent to plan on releasing a 1.0 of winit based on
@tomaka, do I understand correctly that with the right stability guarantees you'd go with |
@pcwalton, do you think a Futures-/Tokio-based event loop for winit would work well for Servo? |
I think that's reasonable, yes, assuming that today's futures-rs seems to cover your needs. We definitely won't be removing any functionality, and at this point the core future and stream abstractions have gotten quite a bit of vetting. While I do expect a 0.2 release in the coming months, we're already using features like deprecation warnings to make that a smooth transition, and I don't expect core abstractions to be changes much, if at all -- with the exception of the just-added |
At first reaction, this seems like it would be fine for Servo. The main parts for us are:
|
This is on my to-do list, probably to look at starting Q1 2017. |
I'm still a bit hesitant about depending on I'm tempted to either wait for the private/shared dependencies system that was advertised in a talk recently, or for futures to be merged in the stdlib. |
I've been thinking about it, and for me the future library has too many drawbacks:
If you except the leakpocalypse-related problem described above, I don't see any good reason to use futures instead of a custom API. On the other hand, it's very easy to wrap the API described in the opening post around futures if one wants to use futures. |
Maybe an API like this is better: let events_loop = EventsLoop::new();
let window1 = WindowBuilder::new().build(&events_loop).unwrap();
let window2 = WindowBuilder::new().build(&events_loop).unwrap();
loop {
events_loop.run_once(|event| {
match event {
Event::Resized { window_id, ... } => {
if window_id == window1.id() { println!("win 1 resized"); }
else { println!("win 2 resized") }
},
Event::MonitorConnected { ... } => {},
_ => {}
}
});
} The events loop calls a local callback and each event contains an identifier of the window that produced the event (amongst the windows that were registered on the events loop). I think it would still provide all the advantages described above. We still need to use a callback and not return events by value because of the MacOS resize callback problem, and for emscripten. Someone would really wants to use futures could easily implement |
Perhaps this is of interest: https://github.com/gamazeps/RobotS |
@tomaka that API looks fine to me! Would it be worth making a new window-specific event enum to differentiate between general events and events that apply to a specific window? It might save adding a pub enum Event {
MonitorConnected,
MonitorDisconnected,
KeyboardInput,
Awakened,
Suspended,
Window { id: WindowId, event: WindowEvent },
}
pub enum WindowEvent {
Focused,
Resized,
Moved,
Closed,
DroppedFile,
MouseMoved,
MouseEntered,
MouseLeft,
Touch,
TouchpadPressure,
} So matching on events might look something like this match event {
winit::Event::MonitorConnected { ... } => (),
winit::Event::KeyboardInput { ... } => (),
winit::Event::Window { id, event } => match event {
winit::WindowEvent::Resized { ... } if id == window1 => println!("win 1 resized"),
winit::WindowEvent::Closed if id == window2 => println!("win 2 closed"),
_ => ()
},
_ => (),
} |
rust-windowing/glutin#850 and rust-windowing/winit#118 provide a fix for missing `Resized` events on OS X. The upstream fix is temporary until a proper solution in relation to rust-windowing/winit#20 can be implemented. The temp fix is basically the same as the workaround that we had here, so there should no longer be any reason to maintain this code.
This is a follow up to the new API introduced in rust-windowing#20. This also fixes the issue where window resize events would not be emitted until the end of the resize. This PR fixese rust-windowing#39 by ensuring that the user callback given to either `EventsLoop::poll_events` or `EventsLoop::run_forever` can be called by each window delegate's resize callback directly.
This is a follow up to the new API introduced in rust-windowing#20. This also fixes the issue where window resize events would not be emitted until the end of the resize. This PR fixese rust-windowing#39 by ensuring that the user callback given to either `EventsLoop::poll_events` or `EventsLoop::run_forever` can be called by each window delegate's resize callback directly.
Winit's and glutin's current API look like this:
I suggest that instead you'd have to register your window to what's called an events loop. Then, you'd call a method on the events loop to run it. Running the events loop would process events received by the operating system, and call a callback previously registered on the window.
This is essentially the same approach as the
future-rs
library, except that we're not using future-rs directly because it's not totally stable. I'd like to eventually publish winit 1.0 (in, like, the next nine months or so), and it's unlikely that future-rs publishes its 1.0 before winit.Example usage with the new API (just to give an overview, the details are not important at the moment):
I've always been a bit wary about callbacks because they are often too "magical", like you don't know which thread calls them and at what moment. But the fact that it's the
run()
method of the events loop that calls the callbacks means that it's a good design in my opinion.This approach would have some benefits over the current system:
Window
object in Rust from the actual window. The actual window could be stored in theEventsLoop
for example.EventsLoop
to run on the main thread for OSX while still allowing theWindow
objects to be shared between threads. This has been a huge safety hole in glutin since the beginning.set_resize_callback
function of MacOS would be gone.WindowProxy
would be gone in favor of an "EventsLoopProxy
".The text was updated successfully, but these errors were encountered: