From 9feada206f6b9fb1e9da118be6b77dfc217ace8d Mon Sep 17 00:00:00 2001 From: Osspial Date: Fri, 13 Jul 2018 01:39:53 -0400 Subject: [PATCH] Update run_forever to hijack thread --- examples/cursor.rs | 28 +++++++++++---- examples/cursor_grab.rs | 2 +- examples/fullscreen.rs | 2 +- examples/handling_close.rs | 2 +- examples/min_max_size.rs | 2 +- examples/multiwindow.rs | 26 ++++++++------ examples/proxy.rs | 2 +- examples/resizable.rs | 2 +- examples/transparent.rs | 2 +- examples/window.rs | 4 +-- examples/window_icon.rs | 2 +- src/lib.rs | 32 ++++++++++------- src/platform/windows/drop_handler.rs | 7 ++-- src/platform/windows/events_loop.rs | 51 ++++++++++++++++------------ 14 files changed, 100 insertions(+), 64 deletions(-) diff --git a/examples/cursor.rs b/examples/cursor.rs index 2eb5e8cba..9feeb76c1 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -1,22 +1,21 @@ extern crate winit; -use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow}; +use winit::{Event, EventLoop, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow}; fn main() { - let mut events_loop = winit::EventLoop::new(); + let mut events_loop = EventLoop::new(); let window = winit::WindowBuilder::new().build(&events_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| { + events_loop.run_forever(move |event, _: &EventLoop| { 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; @@ -30,3 +29,18 @@ fn main() { 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 +]; diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index d36ef72a7..11456f73c 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -8,7 +8,7 @@ fn main() { .build(&events_loop) .unwrap(); - events_loop.run_forever(|event| { + events_loop.run_forever(move |event, _: &winit::EventLoop| { if let winit::Event::WindowEvent { event, .. } = event { use winit::WindowEvent::*; match event { diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index c4cebca36..84abb3518 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -35,7 +35,7 @@ fn main() { let mut is_maximized = false; let mut decorations = true; - events_loop.run_forever(|event| { + events_loop.run_forever(move |event, _: &winit::EventLoop| { println!("{:?}", event); match event { diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 3b4add406..93d655bc6 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -10,7 +10,7 @@ fn main() { let mut close_requested = false; - events_loop.run_forever(|event| { + events_loop.run_forever(move |event, _: &winit::EventLoop| { use winit::WindowEvent::*; use winit::ElementState::Released; use winit::VirtualKeyCode::{N, Y}; diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index 836820e7f..f011920bf 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -12,7 +12,7 @@ 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(|event| { + events_loop.run_forever(move |event, _: &winit::EventLoop| { println!("{:?}", event); match event { diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 5b1d75a96..3dfabf892 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -11,19 +11,25 @@ fn main() { windows.insert(window.id(), window); } - events_loop.run_forever(|event| { + events_loop.run_forever(move |event, events_loop: &winit::EventLoop| { match event { - winit::Event::WindowEvent { - event: winit::WindowEvent::CloseRequested, - window_id, - } => { - println!("Window {:?} has received the signal to close", window_id); + winit::Event::WindowEvent { event, window_id } => { + match event { + winit::WindowEvent::CloseRequested => { + println!("Window {:?} has received the signal to close", window_id); - // This drops the window, causing it to close. - windows.remove(&window_id); + // This drops the window, causing it to close. + windows.remove(&window_id); - if windows.is_empty() { - return winit::ControlFlow::Break; + if windows.is_empty() { + return winit::ControlFlow::Break; + } + }, + winit::WindowEvent::KeyboardInput{..} => { + let window = winit::Window::new(&events_loop).unwrap(); + windows.insert(window.id(), window); + }, + _ => () } } _ => (), diff --git a/examples/proxy.rs b/examples/proxy.rs index 8518c1e21..4df2efe4f 100644 --- a/examples/proxy.rs +++ b/examples/proxy.rs @@ -18,7 +18,7 @@ fn main() { } }); - events_loop.run_forever(|event| { + events_loop.run_forever(move |event, _: &winit::EventLoop| { println!("{:?}", event); match event { winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => diff --git a/examples/resizable.rs b/examples/resizable.rs index fab1c65c8..c84fe5b05 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -12,7 +12,7 @@ fn main() { .build(&events_loop) .unwrap(); - events_loop.run_forever(|event| { + events_loop.run_forever(move |event, _: &winit::EventLoop| { match event { winit::Event::WindowEvent { event, .. } => match event { winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break, diff --git a/examples/transparent.rs b/examples/transparent.rs index c5be1f233..7cfed5056 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -9,7 +9,7 @@ fn main() { window.set_title("A fantastic window!"); - events_loop.run_forever(|event| { + events_loop.run_forever(move |event, _: &winit::EventLoop| { println!("{:?}", event); match event { diff --git a/examples/window.rs b/examples/window.rs index 2b9803378..e46386744 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -1,14 +1,14 @@ 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!") .build(&events_loop) .unwrap(); - events_loop.run_forever(|event| { + events_loop.run_forever(move |event, _: &winit::EventLoop| { println!("{:?}", event); match event { diff --git a/examples/window_icon.rs b/examples/window_icon.rs index 4cd9029c1..fc54c5394 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -30,7 +30,7 @@ fn main() { .build(&events_loop) .unwrap(); - events_loop.run_forever(|event| { + events_loop.run_forever(move |event, _: &EventLoop| { if let winit::Event::WindowEvent { event, .. } = event { use winit::WindowEvent::*; match event { diff --git a/src/lib.rs b/src/lib.rs index 9e60e78d6..bdfdb9928 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,7 @@ //! # use winit::EventLoop; //! # let mut events_loop = EventLoop::new(); //! -//! events_loop.run_forever(|event| { +//! events_loop.run_forever(move |event, _: &EventLoop| { //! match event { //! Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { //! println!("The close button was pressed; stopping"); @@ -139,7 +139,7 @@ pub mod os; /// let mut events_loop = EventLoop::new(); /// let window = Window::new(&events_loop).unwrap(); /// -/// events_loop.run_forever(|event| { +/// events_loop.run_forever(move |event, _: &EventLoop| { /// match event { /// Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { /// ControlFlow::Break @@ -199,6 +199,18 @@ 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. @@ -240,19 +252,13 @@ impl EventLoop { MonitorId { inner: self.events_loop.get_primary_monitor() } } - /// Calls `callback` every time an event is received. If no event is available, sleeps the - /// current thread and waits for an event. If the callback returns `ControlFlow::Break` then - /// `run_forever` will immediately return. + /// Hijacks the calling thread and initializes the `winit` event loop. Can take a + /// `FnMut(Event, &EventLoop) -> ControlFlow` or a custom `EventHandler` type. /// - /// # Danger! - /// - /// The callback is run after *every* event, so if its execution time is non-trivial the event queue may not empty - /// at a sufficient rate. Rendering in the callback with vsync enabled **will** cause significant lag. + /// Any values not passed to this function will *not* be dropped. #[inline] - pub fn run_forever(&mut self, callback: F) - where F: FnMut(Event) -> ControlFlow - { - self.events_loop.run_forever(callback) + pub fn run_forever(self, event_handler: impl 'static + EventHandler) -> ! { + self.events_loop.run_forever(event_handler) } /// Creates an `EventLoopProxy` that can be used to wake up the `EventLoop` from another diff --git a/src/platform/windows/drop_handler.rs b/src/platform/windows/drop_handler.rs index d3f28a9b3..b4357e1a8 100644 --- a/src/platform/windows/drop_handler.rs +++ b/src/platform/windows/drop_handler.rs @@ -5,6 +5,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::{mem, ptr}; use std::rc::Rc; use std::cell::RefCell; +use std::collections::VecDeque; use winapi::ctypes::c_void; use winapi::shared::guiddef::REFIID; @@ -25,7 +26,7 @@ pub struct FileDropHandlerData { pub interface: IDropTarget, refcount: AtomicUsize, window: HWND, - event_queue: Rc>>, + event_queue: Rc>>, } pub struct FileDropHandler { @@ -34,7 +35,7 @@ pub struct FileDropHandler { #[allow(non_snake_case)] impl FileDropHandler { - pub fn new(window: HWND, event_queue: Rc>>) -> FileDropHandler { + pub fn new(window: HWND, event_queue: Rc>>) -> FileDropHandler { let data = Box::new(FileDropHandlerData { interface: IDropTarget { lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl, @@ -187,7 +188,7 @@ impl FileDropHandler { impl FileDropHandlerData { fn send_event(&self, event: Event) { - self.event_queue.borrow_mut().push(event); + self.event_queue.borrow_mut().push_back(event); } } diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index 17d6c526b..5c588550a 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -17,9 +17,8 @@ use winapi::shared::basetsd::UINT_PTR; use std::rc::Rc; use std::{mem, ptr}; use std::cell::RefCell; -use std::ffi::OsString; -use std::os::windows::ffi::OsStringExt; use std::sync::{Arc, Mutex}; +use std::collections::VecDeque; use winapi::ctypes::c_int; use winapi::shared::minwindef::{ @@ -35,14 +34,13 @@ use winapi::shared::minwindef::{ }; use winapi::shared::windef::{HWND, POINT, RECT}; use winapi::shared::windowsx; -use winapi::shared::winerror::S_OK; -use winapi::um::{winuser, processthreadsapi, ole2, shellapi, commctrl}; -use winapi::um::oleidl::LPDROPTARGET; +use winapi::um::{winuser, processthreadsapi, ole2, commctrl}; use winapi::um::winnt::{LONG, LPCSTR, SHORT}; use { ControlFlow, Event, + EventHandler, EventLoopClosed, KeyboardInput, LogicalPosition, @@ -110,13 +108,13 @@ pub struct WindowState { pub(crate) struct SubclassInput { pub window_state: Arc>, - pub event_queue: Rc>>, + pub event_queue: Rc>>, pub file_drop_handler: FileDropHandler } impl SubclassInput { fn send_event(&self, event: Event) { - self.event_queue.borrow_mut().push(event); + self.event_queue.borrow_mut().push_back(event); } } @@ -136,7 +134,7 @@ impl WindowState { pub struct EventLoop { // Id of the background thread from the Win32 API. thread_id: DWORD, - pub(crate) event_queue: Rc>> + pub(crate) event_queue: Rc>> } impl EventLoop { @@ -151,13 +149,15 @@ impl EventLoop { EventLoop { thread_id, - event_queue: Rc::new(RefCell::new(Vec::new())) + event_queue: Rc::new(RefCell::new(VecDeque::new())) } } - pub fn run_forever(&mut self, mut callback: F) - where F: FnMut(Event) -> ControlFlow - { + pub fn run_forever(self, mut event_handler: impl 'static + EventHandler) -> ! { + let event_loop = ::EventLoop { + events_loop: self, + _marker: ::std::marker::PhantomData + }; 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 @@ -166,17 +166,17 @@ impl EventLoop { let mut msg = mem::uninitialized(); - loop { + '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); - return; + break 'main; } match msg.message { x if x == *WAKEUP_MSG_ID => { - if ControlFlow::Break == callback(Event::Awakened) { - return; + if ControlFlow::Break == event_handler.handle_event(Event::Awakened, &event_loop) { + break 'main; } }, x if x == *EXEC_MSG_ID => { @@ -184,20 +184,29 @@ impl EventLoop { function() } _ => { - // Calls `callback` below. + // Calls `event_handler` below. winuser::TranslateMessage(&msg); winuser::DispatchMessageW(&msg); } } - let mut event_queue = self.event_queue.borrow_mut(); - for event in event_queue.drain(..) { - if ControlFlow::Break == callback(event) { - return; + 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 + }; + + if ControlFlow::Break == event_handler.handle_event(event, &event_loop) { + break 'main; } } } } + + drop(event_handler); + ::std::process::exit(0); } pub fn create_proxy(&self) -> EventLoopProxy {