From 02f922f003f56215b92b8feeb9148ad2dd181fc2 Mon Sep 17 00:00:00 2001 From: Osspial Date: Fri, 17 Aug 2018 17:31:04 -0400 Subject: [PATCH] Implement new ControlFlow and associated events --- examples/cursor.rs | 8 +- examples/cursor_grab.rs | 10 +- examples/fullscreen.rs | 11 +- examples/handling_close.rs | 8 +- examples/min_max_size.rs | 9 +- examples/multiwindow.rs | 8 +- examples/proxy.rs | 8 +- examples/resizable.rs | 8 +- examples/transparent.rs | 9 +- examples/window.rs | 6 +- examples/window_icon.rs | 8 +- src/events.rs | 35 +++++++ src/lib.rs | 84 ++++++--------- src/platform/windows/events_loop.rs | 156 ++++++++++++++++++++++------ 14 files changed, 237 insertions(+), 131 deletions(-) diff --git a/examples/cursor.rs b/examples/cursor.rs index 9feeb76c1..81a1a1cd3 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -3,14 +3,14 @@ extern crate winit; use winit::{Event, EventLoop, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow}; fn main() { - let mut events_loop = EventLoop::new(); + let events_loop = EventLoop::new(); let window = winit::WindowBuilder::new().build(&events_loop).unwrap(); window.set_title("A fantastic window!"); let mut cursor_idx = 0; - events_loop.run_forever(move |event, _: &EventLoop| { + events_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]); @@ -22,11 +22,11 @@ fn main() { } }, Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { - return ControlFlow::Break; + *control_flow = ControlFlow::Exit; + return; }, _ => () } - ControlFlow::Continue }); } diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 11456f73c..392a68d2e 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -1,18 +1,19 @@ extern crate winit; fn main() { - let mut events_loop = winit::EventLoop::new(); + let events_loop = winit::EventLoop::new(); let window = winit::WindowBuilder::new() .with_title("Super Cursor Grab'n'Hide Simulator 9000") .build(&events_loop) .unwrap(); - events_loop.run_forever(move |event, _: &winit::EventLoop| { + events_loop.run(move |event, _, control_flow| { + *control_flow = winit::ControlFlow::Wait; if let winit::Event::WindowEvent { event, .. } = event { use winit::WindowEvent::*; match event { - CloseRequested => return winit::ControlFlow::Break, + CloseRequested => *control_flow = winit::ControlFlow::Exit, KeyboardInput { input: winit::KeyboardInput { state: winit::ElementState::Released, @@ -24,7 +25,7 @@ fn main() { } => { use winit::VirtualKeyCode::*; match key { - Escape => return winit::ControlFlow::Break, + Escape => *control_flow = winit::ControlFlow::Exit, G => window.grab_cursor(!modifiers.shift).unwrap(), H => window.hide_cursor(!modifiers.shift), _ => (), @@ -33,6 +34,5 @@ fn main() { _ => (), } } - winit::ControlFlow::Continue }); } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 84abb3518..95afca067 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -4,7 +4,7 @@ use std::io::{self, Write}; use winit::{ControlFlow, Event, WindowEvent}; fn main() { - let mut events_loop = winit::EventLoop::new(); + let events_loop = winit::EventLoop::new(); // enumerating monitors let monitor = { @@ -35,12 +35,13 @@ fn main() { let mut is_maximized = false; let mut decorations = true; - events_loop.run_forever(move |event, _: &winit::EventLoop| { + events_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 { @@ -50,7 +51,7 @@ fn main() { }, .. } => match (virtual_code, state) { - (winit::VirtualKeyCode::Escape, _) => return ControlFlow::Break, + (winit::VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit, (winit::VirtualKeyCode::F, winit::ElementState::Pressed) => { is_fullscreen = !is_fullscreen; if !is_fullscreen { @@ -73,7 +74,5 @@ fn main() { }, _ => {} } - - ControlFlow::Continue }); } diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 93d655bc6..b0189c3b7 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -1,7 +1,7 @@ extern crate winit; fn main() { - let mut events_loop = winit::EventLoop::new(); + let events_loop = winit::EventLoop::new(); let _window = winit::WindowBuilder::new() .with_title("Your faithful window") @@ -10,7 +10,7 @@ fn main() { let mut close_requested = false; - events_loop.run_forever(move |event, _: &winit::EventLoop| { + events_loop.run(move |event, _, control_flow| { use winit::WindowEvent::*; use winit::ElementState::Released; use winit::VirtualKeyCode::{N, Y}; @@ -53,7 +53,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 = winit::ControlFlow::Exit; } } N => { @@ -69,6 +69,6 @@ fn main() { _ => (), } - winit::ControlFlow::Continue + *control_flow = winit::ControlFlow::Wait; }); } diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index f011920bf..88d4823d8 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -3,7 +3,7 @@ extern crate winit; use winit::dpi::LogicalSize; fn main() { - let mut events_loop = winit::EventLoop::new(); + let events_loop = winit::EventLoop::new(); let window = winit::WindowBuilder::new() .build(&events_loop) @@ -12,12 +12,13 @@ fn main() { window.set_min_dimensions(Some(LogicalSize::new(400.0, 200.0))); window.set_max_dimensions(Some(LogicalSize::new(800.0, 400.0))); - events_loop.run_forever(move |event, _: &winit::EventLoop| { + events_loop.run(move |event, _, control_flow| { println!("{:?}", event); match event { - winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => winit::ControlFlow::Break, - _ => winit::ControlFlow::Continue, + winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => + *control_flow = winit::ControlFlow::Exit, + _ => *control_flow = winit::ControlFlow::Wait, } }); } diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 3dfabf892..c4278828d 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -3,7 +3,7 @@ extern crate winit; use std::collections::HashMap; fn main() { - let mut events_loop = winit::EventLoop::new(); + let events_loop = winit::EventLoop::new(); let mut windows = HashMap::new(); for _ in 0..3 { @@ -11,7 +11,8 @@ fn main() { windows.insert(window.id(), window); } - events_loop.run_forever(move |event, events_loop: &winit::EventLoop| { + events_loop.run(move |event, events_loop, control_flow| { + *control_flow = winit::ControlFlow::Wait; match event { winit::Event::WindowEvent { event, window_id } => { match event { @@ -22,7 +23,7 @@ fn main() { windows.remove(&window_id); if windows.is_empty() { - return winit::ControlFlow::Break; + *control_flow = winit::ControlFlow::Exit; } }, winit::WindowEvent::KeyboardInput{..} => { @@ -34,6 +35,5 @@ fn main() { } _ => (), } - winit::ControlFlow::Continue }) } diff --git a/examples/proxy.rs b/examples/proxy.rs index 4df2efe4f..c9f7bf751 100644 --- a/examples/proxy.rs +++ b/examples/proxy.rs @@ -1,7 +1,7 @@ extern crate winit; fn main() { - let mut events_loop = winit::EventLoop::new(); + let events_loop = winit::EventLoop::new(); let _window = winit::WindowBuilder::new() .with_title("A fantastic window!") @@ -18,12 +18,12 @@ fn main() { } }); - events_loop.run_forever(move |event, _: &winit::EventLoop| { + events_loop.run(move |event, _, control_flow| { println!("{:?}", event); match event { winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => - winit::ControlFlow::Break, - _ => winit::ControlFlow::Continue, + *control_flow = winit::ControlFlow::Wait, + _ => *control_flow = winit::ControlFlow::Wait, } }); } diff --git a/examples/resizable.rs b/examples/resizable.rs index c84fe5b05..fd75860d8 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,7 +1,7 @@ extern crate winit; fn main() { - let mut events_loop = winit::EventLoop::new(); + let events_loop = winit::EventLoop::new(); let mut resizable = false; @@ -12,10 +12,11 @@ fn main() { .build(&events_loop) .unwrap(); - events_loop.run_forever(move |event, _: &winit::EventLoop| { + events_loop.run(move |event, _, control_flow| { + *control_flow = winit::ControlFlow::Wait; match event { winit::Event::WindowEvent { event, .. } => match event { - winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break, + winit::WindowEvent::CloseRequested => *control_flow = winit::ControlFlow::Exit, winit::WindowEvent::KeyboardInput { input: winit::KeyboardInput { @@ -33,6 +34,5 @@ fn main() { }, _ => (), }; - winit::ControlFlow::Continue }); } diff --git a/examples/transparent.rs b/examples/transparent.rs index 7cfed5056..321b90716 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -1,7 +1,7 @@ extern crate winit; fn main() { - let mut events_loop = winit::EventLoop::new(); + let events_loop = winit::EventLoop::new(); let window = winit::WindowBuilder::new().with_decorations(false) .with_transparency(true) @@ -9,12 +9,13 @@ fn main() { window.set_title("A fantastic window!"); - events_loop.run_forever(move |event, _: &winit::EventLoop| { + events_loop.run(move |event, _, control_flow| { println!("{:?}", event); match event { - winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => winit::ControlFlow::Break, - _ => winit::ControlFlow::Continue, + winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => + *control_flow = winit::ControlFlow::Exit, + _ => *control_flow = winit::ControlFlow::Wait, } }); } diff --git a/examples/window.rs b/examples/window.rs index e46386744..5c6f753e8 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -8,15 +8,15 @@ fn main() { .build(&events_loop) .unwrap(); - events_loop.run_forever(move |event, _: &winit::EventLoop| { + events_loop.run(move |event, _, control_flow| { println!("{:?}", event); match event { winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. - } => winit::ControlFlow::Break, - _ => winit::ControlFlow::Continue, + } => *control_flow = winit::ControlFlow::Exit, + _ => *control_flow = winit::ControlFlow::Wait, } }); } diff --git a/examples/window_icon.rs b/examples/window_icon.rs index fc54c5394..691ec09ad 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -20,7 +20,7 @@ fn main() { // feature enabled). let icon = Icon::from_path(path).expect("Failed to open icon"); - let mut events_loop = winit::EventLoop::new(); + let events_loop = winit::EventLoop::new(); let window = winit::WindowBuilder::new() .with_title("An iconic window!") @@ -30,11 +30,12 @@ fn main() { .build(&events_loop) .unwrap(); - events_loop.run_forever(move |event, _: &EventLoop| { + events_loop.run(move |event, _, control_flow| { + *control_flow = winit::ControlFlow::Wait; if let winit::Event::WindowEvent { event, .. } = event { use winit::WindowEvent::*; match event { - CloseRequested => return winit::ControlFlow::Break, + CloseRequested => *control_flow = winit::ControlFlow::Exit, DroppedFile(path) => { use image::GenericImageView; @@ -81,7 +82,6 @@ fn main() { _ => (), } } - winit::ControlFlow::Continue }); } diff --git a/src/events.rs b/src/events.rs index c740c2977..8951ed6f2 100644 --- a/src/events.rs +++ b/src/events.rs @@ -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 + }, + + /// 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 { diff --git a/src/lib.rs b/src/lib.rs index 0cd3309d7..cce680b14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 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(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 diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index a206df6dc..dac7ca2ad 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -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(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::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::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 {