Implement new ControlFlow and associated events

This commit is contained in:
Osspial
2018-08-17 17:31:04 -04:00
parent 8b8a7675ec
commit 02f922f003
14 changed files with 237 additions and 131 deletions

View File

@@ -1,3 +1,4 @@
use std::time::{Duration, Instant};
use std::path::PathBuf;
use {DeviceId, LogicalPosition, LogicalSize, WindowId};
@@ -14,6 +15,15 @@ pub enum Event {
event: DeviceEvent,
},
Awakened,
/// Emitted when new events arrive from the OS to be processed.
NewEvents(StartCause),
/// Emitted when all of the event loop's events have been processed and control flow is about
/// to be taken away from the program.
EventsCleared,
/// Emitted when the event loop is being shut down. This is irreversable - if this event is
/// emitted, it is guaranteed to be the last event emitted.
LoopDestroyed,
/// The application has been suspended or resumed.
///
@@ -21,6 +31,31 @@ pub enum Event {
Suspended(bool),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StartCause {
/// 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. The actual
/// duration is guaranteed to be greater than or equal to the requested timeout.
TimeoutExpired {
start: Instant,
requested_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 if the event loop is being resumed after the loop's control flow was set to
/// `ControlFlow::Poll`.
Poll,
/// Sent once, immediately after `run` is called. Indicates that the loop was just initialized.
Init
}
/// Describes an event from a `Window`.
#[derive(Clone, Debug, PartialEq)]
pub enum WindowEvent {

View File

@@ -28,47 +28,21 @@
//! The events generated by a window can be retreived from the `EventLoop` the window was created
//! with.
//!
//! There are two ways to do so. The first is to call `events_loop.poll_events(...)`, which will
//! retrieve all the events pending on the windows and immediately return after no new event is
//! available. You usually want to use this method in application that render continuously on the
//! screen, such as video games.
//!
//! ```no_run
//! use winit::{Event, WindowEvent};
//! use winit::dpi::LogicalSize;
//! # use winit::EventLoop;
//! # let mut events_loop = EventLoop::new();
//!
//! loop {
//! events_loop.poll_events(|event| {
//! match event {
//! Event::WindowEvent {
//! event: WindowEvent::Resized(LogicalSize { width, height }),
//! ..
//! } => {
//! println!("The window was resized to {}x{}", width, height);
//! },
//! _ => ()
//! }
//! });
//! }
//! ```
//!
//! The second way is to call `events_loop.run_forever(...)`. As its name tells, it will run
//! forever unless it is stopped by returning `ControlFlow::Break`.
//! You do this by calling `events_loop.run(...)`. This function will run forever unless it is
//! stopped by returning `ControlFlow::Exit`, at which point the entire program will terminate.
//!
//! ```no_run
//! use winit::{ControlFlow, Event, WindowEvent};
//! # use winit::EventLoop;
//! # let mut events_loop = EventLoop::new();
//! # let events_loop = EventLoop::new();
//!
//! events_loop.run_forever(move |event, _: &EventLoop| {
//! events_loop.run(move |event, _, control_flow| {
//! match event {
//! Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
//! println!("The close button was pressed; stopping");
//! ControlFlow::Break
//! *control_flow = ControlFlow::Exit
//! },
//! _ => ControlFlow::Continue,
//! _ => *control_flow = ControlFlow::Wait,
//! }
//! });
//! ```
@@ -116,6 +90,7 @@ extern crate percent_encoding;
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
extern crate smithay_client_toolkit as sctk;
use std::time::Duration;
pub(crate) use dpi::*; // TODO: Actually change the imports throughout the codebase.
pub use events::*;
pub use window::{AvailableMonitorsIter, MonitorId};
@@ -139,12 +114,12 @@ pub mod os;
/// let mut events_loop = EventLoop::new();
/// let window = Window::new(&events_loop).unwrap();
///
/// events_loop.run_forever(move |event, _: &EventLoop| {
/// events_loop.run(move |event, _, control_flow| {
/// match event {
/// Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
/// ControlFlow::Break
/// *control_flow = ControlFlow::Exit
/// },
/// _ => ControlFlow::Continue,
/// _ => *control_flow = ControlFlow::Wait,
/// }
/// });
/// ```
@@ -199,28 +174,29 @@ impl std::fmt::Debug for EventLoop {
}
}
pub trait EventHandler {
fn handle_event(&mut self, event: Event, event_loop: &EventLoop) -> ControlFlow;
}
impl<F> EventHandler for F
where F: FnMut(Event, &EventLoop) -> ControlFlow
{
fn handle_event(&mut self, event: Event, event_loop: &EventLoop) -> ControlFlow {
self(event, event_loop)
}
}
/// Returned by the user callback given to the `EventLoop::run_forever` method.
///
/// Indicates whether the `run_forever` method should continue or complete.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ControlFlow {
/// Continue looping and waiting for events.
Continue,
/// Break from the event loop.
Break,
/// When the current loop iteration finishes, suspend the thread until another event arrives.
Wait,
/// When the current loop iteration finishes, suspend the thread until either another event
/// arrives or the timeout expires.
WaitTimeout(Duration),
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
/// whether or not new events are available to process.
Poll,
/// Send a `LoopDestroyed` event and stop the event loop.
Exit
}
impl Default for ControlFlow {
#[inline(always)]
fn default() -> ControlFlow {
ControlFlow::Poll
}
}
impl EventLoop {
@@ -257,8 +233,10 @@ impl EventLoop {
///
/// Any values not passed to this function will *not* be dropped.
#[inline]
pub fn run_forever(self, event_handler: impl 'static + EventHandler) -> ! {
self.events_loop.run_forever(event_handler)
pub fn run<F>(self, event_handler: F) -> !
where F: 'static + FnMut(Event, &EventLoop, &mut ControlFlow)
{
self.events_loop.run(event_handler)
}
/// Creates an `EventLoopProxy` that can be used to wake up the `EventLoop` from another

View File

@@ -19,6 +19,7 @@ use std::{mem, ptr};
use std::cell::RefCell;
use std::sync::Arc;
use std::collections::VecDeque;
use std::time::{Duration, Instant};
use parking_lot::Mutex;
use winapi::ctypes::c_int;
@@ -35,13 +36,12 @@ use winapi::shared::minwindef::{
};
use winapi::shared::windef::{HWND, POINT, RECT};
use winapi::shared::windowsx;
use winapi::um::{winuser, processthreadsapi, ole2, commctrl};
use winapi::um::{winuser, winbase, processthreadsapi, ole2, commctrl};
use winapi::um::winnt::{LONG, LPCSTR, SHORT};
use {
ControlFlow,
Event,
EventHandler,
EventLoopClosed,
KeyboardInput,
LogicalPosition,
@@ -50,7 +50,7 @@ use {
WindowEvent,
WindowId as SuperWindowId,
};
use events::{DeviceEvent, Touch, TouchPhase};
use events::{DeviceEvent, Touch, TouchPhase, StartCause};
use platform::platform::{event, Cursor, WindowId, DEVICE_ID, wrap_device_id, util};
use platform::platform::dpi::{
become_dpi_aware,
@@ -154,11 +154,16 @@ impl EventLoop {
}
}
pub fn run_forever(self, mut event_handler: impl 'static + EventHandler) -> ! {
pub fn run<F>(self, mut event_handler: F) -> !
where F: 'static + FnMut(Event, &::EventLoop, &mut ControlFlow)
{
let event_loop = ::EventLoop {
events_loop: self,
_marker: ::std::marker::PhantomData
};
let mut control_flow = ControlFlow::default();
let mut timer_handle = 0;
unsafe {
// Calling `PostThreadMessageA` on a thread that does not have an events queue yet
// will fail. In order to avoid this situation, we call `IsGuiThread` to initialize
@@ -168,44 +173,109 @@ impl EventLoop {
let mut msg = mem::uninitialized();
'main: loop {
if winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
// Only happens if the message is `WM_QUIT`.
debug_assert_eq!(msg.message, winuser::WM_QUIT);
break 'main;
}
match msg.message {
x if x == *WAKEUP_MSG_ID => {
if ControlFlow::Break == event_handler.handle_event(Event::Awakened, &event_loop) {
macro_rules! call_event_handler {
($event:expr) => {{
event_handler($event, &event_loop, &mut control_flow);
if ControlFlow::Exit == control_flow {
break 'main;
}
},
x if x == *EXEC_MSG_ID => {
let mut function: Box<Box<FnMut()>> = Box::from_raw(msg.wParam as usize as *mut _);
function()
}
_ => {
// Calls `event_handler` below.
winuser::TranslateMessage(&msg);
winuser::DispatchMessageW(&msg);
}
}}
}
loop {
// For whatever reason doing this in a `whlie let` loop doesn't drop the `RefMut`,
// so we have to do it like this.
let event = match event_loop.events_loop.event_queue.borrow_mut().pop_front() {
Some(event) => event,
None => break
};
let mut has_message = true;
let new_events_cause: StartCause;
match control_flow {
ControlFlow::Wait => {
let wait_start = Instant::now();
if winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
// Only happens if the message is `WM_QUIT`.
debug_assert_eq!(msg.message, winuser::WM_QUIT);
break 'main;
}
new_events_cause =
StartCause::WaitCancelled {
start: wait_start,
requested_duration: None
};
}
ControlFlow::WaitTimeout(duration) => {
let new_handle = winuser::SetTimer(ptr::null_mut(), timer_handle, dur2timeout(duration), None);
if timer_handle == 0 {
timer_handle = new_handle;
}
let wait_start = Instant::now();
if winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
// Only happens if the message is `WM_QUIT`.
debug_assert_eq!(msg.message, winuser::WM_QUIT);
break 'main;
}
if msg.message == winuser::WM_TIMER && msg.wParam == timer_handle {
new_events_cause =
StartCause::TimeoutExpired {
start: wait_start,
requested_duration: duration,
};
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) {
has_message = false;
}
} else {
new_events_cause =
StartCause::WaitCancelled {
start: wait_start,
requested_duration: Some(duration)
};
}
}
ControlFlow::Poll => {
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) {
has_message = false;
}
new_events_cause = StartCause::Poll;
}
ControlFlow::Exit => break 'main
}
call_event_handler!(Event::NewEvents(new_events_cause));
if ControlFlow::Break == event_handler.handle_event(event, &event_loop) {
break 'main;
while has_message {
match msg.message {
x if x == *WAKEUP_MSG_ID => {
call_event_handler!(Event::Awakened);
},
x if x == *EXEC_MSG_ID => {
let mut function: Box<Box<FnMut()>> = Box::from_raw(msg.wParam as usize as *mut _);
function()
}
_ => {
// Calls `event_handler` below.
winuser::TranslateMessage(&msg);
winuser::DispatchMessageW(&msg);
}
}
loop {
// For whatever reason doing this in a `whlie let` loop doesn't drop the `RefMut`,
// so we have to do it like this.
let event = match event_loop.events_loop.event_queue.borrow_mut().pop_front() {
Some(event) => event,
None => break
};
call_event_handler!(event);
}
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) {
has_message = false;
}
}
call_event_handler!(Event::EventsCleared);
}
if timer_handle != 0 {
winuser::KillTimer(ptr::null_mut(), timer_handle);
}
}
event_handler(Event::LoopDestroyed, &event_loop, &mut control_flow);
drop(event_handler);
::std::process::exit(0);
}
@@ -217,6 +287,28 @@ impl EventLoop {
}
}
// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs
pub fn dur2timeout(dur: Duration) -> DWORD {
// Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
// timeouts in windows APIs are typically u32 milliseconds. To translate, we
// have two pieces to take care of:
//
// * Nanosecond precision is rounded up
// * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE
// (never time out).
dur.as_secs().checked_mul(1000).and_then(|ms| {
ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000)
}).and_then(|ms| {
ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 {1} else {0})
}).map(|ms| {
if ms > DWORD::max_value() as u64 {
winbase::INFINITE
} else {
ms as DWORD
}
}).unwrap_or(winbase::INFINITE)
}
impl Drop for EventLoop {
fn drop(&mut self) {
unsafe {