Skip to content

Commit

Permalink
Event Loop 2.0 API and Windows implementation (rust-windowing#638)
Browse files Browse the repository at this point in the history
* Rename EventsLoop and associated types to EventLoop

* Rename WindowEvent::Refresh to WindowEvent::Redraw

* Remove second thread from win32 backend

* Update run_forever to hijack thread

* Replace windows Mutex with parking_lot Mutex

* Implement new ControlFlow and associated events

* Add StartCause::Init support, timer example

* Add ability to send custom user events

* Fully invert windows control flow so win32 calls into winit's callback

* Add request_redraw

* Rename platform to platform_impl

* Rename os to platform, add Ext trait postfixes

* Add platform::desktop module with EventLoopExt::run_return

* Re-organize into module structure

* Improve documentation

* Small changes to examples

* Improve docs for run and run_return

* Change instances of "events_loop" to "event_loop"

* Rename MonitorId to MonitorHandle

* Add CHANGELOG entry

* Improve WaitUntil timer precision

* When SendEvent is called during event closure, buffer events

* Fix resize lag when waiting in some situations

* Update send test and errors that broke some examples/APIs

* Improve clarity/fix typos in docs

* Fix unreachable panic after setting ControlFlow to Poll during some RedrawRequested events.

* Fix crash when running in release mode

* Remove crossbeam dependency and make drop events work again

* Remove serde implementations from ControlFlow

* Fix 1.24.1 build

* Fix freeze when setting decorations

* Replace &EventLoop in callback with &EventLoopWindowTarget

* Document and implement Debug for EventLoopWindowTarget

* Fix some deadlocks that could occur when changing window state

* Fix thread executor not executing closure when called from non-loop thread

* Fix buffered events not getting dispatched

* Fix crash with runner refcell not getting dropped

* Address review feedback

* Fix CHANGELOG typo

* Catch panics in user callback
  • Loading branch information
Osspial authored and kosyak committed Jul 10, 2019
1 parent e5ab8e5 commit d7c3ee7
Show file tree
Hide file tree
Showing 92 changed files with 3,490 additions and 1,304 deletions.
52 changes: 37 additions & 15 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,41 @@
# Unreleased

# Version 0.19.1 (2019-04-08)

- On Wayland, added a `get_wayland_display` function to `EventsLoopExt`.
- On Windows, fix `CursorMoved(0, 0)` getting dispatched on window focus.
- On macOS, fix command key event left and right reverse.
- On FreeBSD, NetBSD, and OpenBSD, fix build of X11 backend.
- On Windows, fix icon not showing up in corner of window.
- On X11, change DPI scaling factor behavior. First, winit tries to read it from "Xft.dpi" XResource, and uses DPI calculation from xrandr dimensions as fallback behavior.

# Version 0.19.0 (2019-03-06)

- On X11, we will use the faster `XRRGetScreenResourcesCurrent` function instead of `XRRGetScreenResources` when available.
- On macOS, fix keycodes being incorrect when using a non-US keyboard layout.
- On Wayland, fix `with_title()` not setting the windows title
- Changes below are considered **breaking**.
- Change all occurrences of `EventsLoop` to `EventLoop`.
- Previously flat API is now exposed through `event`, `event_loop`, `monitor`, and `window` modules.
- `os` module changes:
- Renamed to `platform`.
- All traits now have platform-specific suffixes.
- Exposes new `desktop` module on Windows, Mac, and Linux.
- Changes to event loop types:
- `EventLoopProxy::wakeup` has been removed in favor of `send_event`.
- **Major:** New `run` method drives winit event loop.
- Returns `!` to ensure API behaves identically across all supported platforms.
- This allows `emscripten` implementation to work without lying about the API.
- `ControlFlow`'s variants have been replaced with `Wait`, `WaitUntil(Instant)`, `Poll`, and `Exit`.
- Is read after `EventsCleared` is processed.
- `Wait` waits until new events are available.
- `WaitUntil` waits until either new events are available or the provided time has been reached.
- `Poll` instantly resumes the event loop.
- `Exit` aborts the event loop.
- Takes a closure that implements `'static + FnMut(Event<T>, &EventLoop<T>, &mut ControlFlow)`.
- `&EventLoop<T>` is provided to allow new `Window`s to be created.
- **Major:** `platform::desktop` module exposes `EventLoopExtDesktop` trait with `run_return` method.
- Behaves identically to `run`, but returns control flow to the calling context and can take non-`'static` closures.
- `EventLoop`'s `poll_events` and `run_forever` methods have been removed in favor of `run` and `run_return`.
- Changes to events:
- Remove `Event::Awakened` in favor of `Event::UserEvent(T)`.
- Can be sent with `EventLoopProxy::send_event`.
- Rename `WindowEvent::Refresh` to `WindowEvent::RedrawRequested`.
- `RedrawRequested` can be sent by the user with the `Window::request_redraw` method.
- `EventLoop`, `EventLoopProxy`, and `Event` are now generic over `T`, for use in `UserEvent`.
- **Major:** Add `NewEvents(StartCause)`, `EventsCleared`, and `LoopDestroyed` variants to `Event`.
- `NewEvents` is emitted when new events are ready to be processed by event loop.
- `StartCause` describes why new events are available, with `ResumeTimeReached`, `Poll`, `WaitCancelled`, and `Init` (sent once at start of loop).
- `EventsCleared` is emitted when all available events have been processed.
- Can be used to perform logic that depends on all events being processed (e.g. an iteration of a game loop).
- `LoopDestroyed` is emitted when the `run` or `run_return` method is about to exit.
- Rename `MonitorId` to `MonitorHandle`.
- Removed `serde` implementations from `ControlFlow`.
- On Wayland, add `set_wayland_theme()` to control client decoration color theme
- Added serde serialization to `os::unix::XWindowType`.
- **Breaking:** `image` crate upgraded to 0.21. This is exposed as part of the `icon_loading` API.
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ bitflags = "1"
version = "0.3.6"
features = [
"combaseapi",
"commctrl",
"dwmapi",
"errhandlingapi",
"hidusage",
Expand Down Expand Up @@ -70,3 +71,6 @@ parking_lot = "0.7"
percent-encoding = "1.0"
gbm = { git = "https://github.com/kosyak/gbm.rs", default-features = false, features = ["drm-support", "import-egl", "gen"] }
drm = { git = "https://github.com/kosyak/drm-rs", branch = "master" }

[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "windows"))'.dependencies.parking_lot]
version = "0.7"
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ another library.
extern crate winit;

fn main() {
let mut events_loop = winit::EventsLoop::new();
let window = winit::Window::new(&events_loop).unwrap();
let mut event_loop = winit::EventLoop::new();
let window = winit::Window::new(&event_loop).unwrap();

events_loop.run_forever(|event| {
event_loop.run(|event| {
match event {
winit::Event::WindowEvent {
event: winit::WindowEvent::CloseRequested,
Expand Down
36 changes: 26 additions & 10 deletions examples/cursor.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
extern crate winit;

use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow};
use winit::window::{WindowBuilder, MouseCursor};
use winit::event::{Event, WindowEvent, ElementState, KeyboardInput};
use winit::event_loop::{EventLoop, ControlFlow};

fn main() {
let mut events_loop = winit::EventsLoop::new();
let event_loop = EventLoop::new();

let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
window.set_title("A fantastic window!");

let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize];
let mut cursor_idx = 0;

events_loop.run_forever(|event| {
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => {
println!("Setting cursor to \"{:?}\"", cursors[cursor_idx]);
window.set_cursor(cursors[cursor_idx]);
if cursor_idx < cursors.len() - 1 {
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
window.set_cursor(CURSORS[cursor_idx]);
if cursor_idx < CURSORS.len() - 1 {
cursor_idx += 1;
} else {
cursor_idx = 0;
}
},
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
return ControlFlow::Break;
*control_flow = ControlFlow::Exit;
return;
},
_ => ()
}
ControlFlow::Continue
});
}

const CURSORS: &[MouseCursor] = &[
MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand,
MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text,
MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress,
MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::Cell,
MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy,
MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing,
MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut,
MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize,
MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize,
MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize,
MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize,
MouseCursor::ColResize, MouseCursor::RowResize
];
29 changes: 16 additions & 13 deletions examples/cursor_grab.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
extern crate winit;

use winit::window::WindowBuilder;
use winit::event::{Event, WindowEvent, ElementState, KeyboardInput};
use winit::event_loop::{EventLoop, ControlFlow};

fn main() {
let mut events_loop = winit::EventsLoop::new();
let event_loop = EventLoop::new();

let window = winit::WindowBuilder::new()
let window = WindowBuilder::new()
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
.build(&events_loop)
.build(&event_loop)
.unwrap();

events_loop.run_forever(|event| {
if let winit::Event::WindowEvent { event, .. } = event {
use winit::WindowEvent::*;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
if let Event::WindowEvent { event, .. } = event {
match event {
CloseRequested => return winit::ControlFlow::Break,
KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Released,
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput {
input: KeyboardInput {
state: ElementState::Released,
virtual_keycode: Some(key),
modifiers,
..
},
..
} => {
use winit::VirtualKeyCode::*;
use winit::event::VirtualKeyCode::*;
match key {
Escape => return winit::ControlFlow::Break,
Escape => *control_flow = ControlFlow::Exit,
G => window.grab_cursor(!modifiers.shift).unwrap(),
H => window.hide_cursor(!modifiers.shift),
_ => (),
Expand All @@ -33,6 +37,5 @@ fn main() {
_ => (),
}
}
winit::ControlFlow::Continue
});
}
38 changes: 20 additions & 18 deletions examples/fullscreen.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
extern crate winit;

use std::io::{self, Write};
use winit::{ControlFlow, Event, WindowEvent};
use winit::monitor::MonitorHandle;
use winit::window::WindowBuilder;
use winit::event::{Event, WindowEvent, VirtualKeyCode, ElementState, KeyboardInput};
use winit::event_loop::{EventLoop, ControlFlow};

fn main() {
let mut events_loop = winit::EventsLoop::new();
let event_loop = EventLoop::new();

#[cfg(target_os = "macos")]
let mut macos_use_simple_fullscreen = false;
Expand All @@ -26,43 +29,44 @@ fn main() {

// Prompt for monitor when using native fullscreen
if !macos_use_simple_fullscreen {
Some(prompt_for_monitor(&events_loop))
Some(prompt_for_monitor(&event_loop))
} else {
None
}
}

#[cfg(not(target_os = "macos"))]
Some(prompt_for_monitor(&events_loop))
Some(prompt_for_monitor(&event_loop))
};

let mut is_fullscreen = monitor.is_some();
let mut is_maximized = false;
let mut decorations = true;

let window = winit::WindowBuilder::new()
let window = WindowBuilder::new()
.with_title("Hello world!")
.with_fullscreen(monitor)
.build(&events_loop)
.build(&event_loop)
.unwrap();

events_loop.run_forever(|event| {
event_loop.run(move |event, _, control_flow| {
println!("{:?}", event);
*control_flow = ControlFlow::Wait;

match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => return ControlFlow::Break,
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput {
input:
winit::KeyboardInput {
KeyboardInput {
virtual_keycode: Some(virtual_code),
state,
..
},
..
} => match (virtual_code, state) {
(winit::VirtualKeyCode::Escape, _) => return ControlFlow::Break,
(winit::VirtualKeyCode::F, winit::ElementState::Pressed) => {
(VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit,
(VirtualKeyCode::F, ElementState::Pressed) => {
#[cfg(target_os = "macos")]
{
if macos_use_simple_fullscreen {
Expand All @@ -82,11 +86,11 @@ fn main() {
window.set_fullscreen(Some(window.get_current_monitor()));
}
}
(winit::VirtualKeyCode::M, winit::ElementState::Pressed) => {
(VirtualKeyCode::M, ElementState::Pressed) => {
is_maximized = !is_maximized;
window.set_maximized(is_maximized);
}
(winit::VirtualKeyCode::D, winit::ElementState::Pressed) => {
(VirtualKeyCode::D, ElementState::Pressed) => {
decorations = !decorations;
window.set_decorations(decorations);
}
Expand All @@ -96,14 +100,12 @@ fn main() {
},
_ => {}
}

ControlFlow::Continue
});
}

// Enumerate monitors and prompt user to choose one
fn prompt_for_monitor(events_loop: &winit::EventsLoop) -> winit::MonitorId {
for (num, monitor) in events_loop.get_available_monitors().enumerate() {
fn prompt_for_monitor(event_loop: &EventLoop<()>) -> MonitorHandle {
for (num, monitor) in event_loop.get_available_monitors().enumerate() {
println!("Monitor #{}: {:?}", num, monitor.get_name());
}

Expand All @@ -113,7 +115,7 @@ fn prompt_for_monitor(events_loop: &winit::EventsLoop) -> winit::MonitorId {
let mut num = String::new();
io::stdin().read_line(&mut num).unwrap();
let num = num.trim().parse().ok().expect("Please enter a number");
let monitor = events_loop.get_available_monitors().nth(num).expect("Please enter a valid ID");
let monitor = event_loop.get_available_monitors().nth(num).expect("Please enter a valid ID");

println!("Using {:?}", monitor.get_name());

Expand Down
30 changes: 16 additions & 14 deletions examples/handling_close.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
extern crate winit;

use winit::window::WindowBuilder;
use winit::event::{Event, WindowEvent, KeyboardInput};
use winit::event_loop::{EventLoop, ControlFlow};

fn main() {
let mut events_loop = winit::EventsLoop::new();
let event_loop = EventLoop::new();

let _window = winit::WindowBuilder::new()
let _window = WindowBuilder::new()
.with_title("Your faithful window")
.build(&events_loop)
.build(&event_loop)
.unwrap();

let mut close_requested = false;

events_loop.run_forever(|event| {
use winit::WindowEvent::*;
use winit::ElementState::Released;
use winit::VirtualKeyCode::{N, Y};
event_loop.run(move |event, _, control_flow| {
use winit::event::ElementState::Released;
use winit::event::VirtualKeyCode::{N, Y};
*control_flow = ControlFlow::Wait;

match event {
winit::Event::WindowEvent { event, .. } => match event {
CloseRequested => {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => {
// `CloseRequested` is sent when the close button on the window is pressed (or
// through whatever other mechanisms the window manager provides for closing a
// window). If you don't handle this event, the close button won't actually do
Expand All @@ -34,9 +38,9 @@ fn main() {
// closing the window. How to close the window is detailed in the handler for
// the Y key.
}
KeyboardInput {
WindowEvent::KeyboardInput {
input:
winit::KeyboardInput {
KeyboardInput {
virtual_keycode: Some(virtual_code),
state: Released,
..
Expand All @@ -53,7 +57,7 @@ fn main() {
// event loop (i.e. if it's a multi-window application), you need to
// drop the window. That closes it, and results in `Destroyed` being
// sent.
return winit::ControlFlow::Break;
*control_flow = ControlFlow::Exit;
}
}
N => {
Expand All @@ -68,7 +72,5 @@ fn main() {
},
_ => (),
}

winit::ControlFlow::Continue
});
}
Loading

0 comments on commit d7c3ee7

Please sign in to comment.