Update run_forever to hijack thread

This commit is contained in:
Osspial
2018-07-13 01:39:53 -04:00
parent 2e83bac99c
commit 9feada206f
14 changed files with 100 additions and 64 deletions

View File

@@ -1,22 +1,21 @@
extern crate winit; extern crate winit;
use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow}; use winit::{Event, EventLoop, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow};
fn main() { fn main() {
let mut events_loop = winit::EventLoop::new(); let mut events_loop = EventLoop::new();
let window = winit::WindowBuilder::new().build(&events_loop).unwrap(); let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
window.set_title("A fantastic window!"); 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; let mut cursor_idx = 0;
events_loop.run_forever(|event| { events_loop.run_forever(move |event, _: &EventLoop| {
match event { match event {
Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => { Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => {
println!("Setting cursor to \"{:?}\"", cursors[cursor_idx]); println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
window.set_cursor(cursors[cursor_idx]); window.set_cursor(CURSORS[cursor_idx]);
if cursor_idx < cursors.len() - 1 { if cursor_idx < CURSORS.len() - 1 {
cursor_idx += 1; cursor_idx += 1;
} else { } else {
cursor_idx = 0; cursor_idx = 0;
@@ -30,3 +29,18 @@ fn main() {
ControlFlow::Continue 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
];

View File

@@ -8,7 +8,7 @@ fn main() {
.build(&events_loop) .build(&events_loop)
.unwrap(); .unwrap();
events_loop.run_forever(|event| { events_loop.run_forever(move |event, _: &winit::EventLoop| {
if let winit::Event::WindowEvent { event, .. } = event { if let winit::Event::WindowEvent { event, .. } = event {
use winit::WindowEvent::*; use winit::WindowEvent::*;
match event { match event {

View File

@@ -35,7 +35,7 @@ fn main() {
let mut is_maximized = false; let mut is_maximized = false;
let mut decorations = true; let mut decorations = true;
events_loop.run_forever(|event| { events_loop.run_forever(move |event, _: &winit::EventLoop| {
println!("{:?}", event); println!("{:?}", event);
match event { match event {

View File

@@ -10,7 +10,7 @@ fn main() {
let mut close_requested = false; let mut close_requested = false;
events_loop.run_forever(|event| { events_loop.run_forever(move |event, _: &winit::EventLoop| {
use winit::WindowEvent::*; use winit::WindowEvent::*;
use winit::ElementState::Released; use winit::ElementState::Released;
use winit::VirtualKeyCode::{N, Y}; use winit::VirtualKeyCode::{N, Y};

View File

@@ -12,7 +12,7 @@ fn main() {
window.set_min_dimensions(Some(LogicalSize::new(400.0, 200.0))); window.set_min_dimensions(Some(LogicalSize::new(400.0, 200.0)));
window.set_max_dimensions(Some(LogicalSize::new(800.0, 400.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); println!("{:?}", event);
match event { match event {

View File

@@ -11,19 +11,25 @@ fn main() {
windows.insert(window.id(), window); windows.insert(window.id(), window);
} }
events_loop.run_forever(|event| { events_loop.run_forever(move |event, events_loop: &winit::EventLoop| {
match event { match event {
winit::Event::WindowEvent { winit::Event::WindowEvent { event, window_id } => {
event: winit::WindowEvent::CloseRequested, match event {
window_id, winit::WindowEvent::CloseRequested => {
} => { println!("Window {:?} has received the signal to close", window_id);
println!("Window {:?} has received the signal to close", window_id);
// This drops the window, causing it to close. // This drops the window, causing it to close.
windows.remove(&window_id); windows.remove(&window_id);
if windows.is_empty() { if windows.is_empty() {
return winit::ControlFlow::Break; return winit::ControlFlow::Break;
}
},
winit::WindowEvent::KeyboardInput{..} => {
let window = winit::Window::new(&events_loop).unwrap();
windows.insert(window.id(), window);
},
_ => ()
} }
} }
_ => (), _ => (),

View File

@@ -18,7 +18,7 @@ fn main() {
} }
}); });
events_loop.run_forever(|event| { events_loop.run_forever(move |event, _: &winit::EventLoop| {
println!("{:?}", event); println!("{:?}", event);
match event { match event {
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } =>

View File

@@ -12,7 +12,7 @@ fn main() {
.build(&events_loop) .build(&events_loop)
.unwrap(); .unwrap();
events_loop.run_forever(|event| { events_loop.run_forever(move |event, _: &winit::EventLoop| {
match event { match event {
winit::Event::WindowEvent { event, .. } => match event { winit::Event::WindowEvent { event, .. } => match event {
winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break, winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break,

View File

@@ -9,7 +9,7 @@ fn main() {
window.set_title("A fantastic window!"); window.set_title("A fantastic window!");
events_loop.run_forever(|event| { events_loop.run_forever(move |event, _: &winit::EventLoop| {
println!("{:?}", event); println!("{:?}", event);
match event { match event {

View File

@@ -1,14 +1,14 @@
extern crate winit; extern crate winit;
fn main() { fn main() {
let mut events_loop = winit::EventLoop::new(); let events_loop = winit::EventLoop::new();
let _window = winit::WindowBuilder::new() let _window = winit::WindowBuilder::new()
.with_title("A fantastic window!") .with_title("A fantastic window!")
.build(&events_loop) .build(&events_loop)
.unwrap(); .unwrap();
events_loop.run_forever(|event| { events_loop.run_forever(move |event, _: &winit::EventLoop| {
println!("{:?}", event); println!("{:?}", event);
match event { match event {

View File

@@ -30,7 +30,7 @@ fn main() {
.build(&events_loop) .build(&events_loop)
.unwrap(); .unwrap();
events_loop.run_forever(|event| { events_loop.run_forever(move |event, _: &EventLoop| {
if let winit::Event::WindowEvent { event, .. } = event { if let winit::Event::WindowEvent { event, .. } = event {
use winit::WindowEvent::*; use winit::WindowEvent::*;
match event { match event {

View File

@@ -62,7 +62,7 @@
//! # use winit::EventLoop; //! # use winit::EventLoop;
//! # let mut events_loop = EventLoop::new(); //! # let mut events_loop = EventLoop::new();
//! //!
//! events_loop.run_forever(|event| { //! events_loop.run_forever(move |event, _: &EventLoop| {
//! match event { //! match event {
//! Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { //! Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
//! println!("The close button was pressed; stopping"); //! println!("The close button was pressed; stopping");
@@ -139,7 +139,7 @@ pub mod os;
/// let mut events_loop = EventLoop::new(); /// let mut events_loop = EventLoop::new();
/// let window = Window::new(&events_loop).unwrap(); /// let window = Window::new(&events_loop).unwrap();
/// ///
/// events_loop.run_forever(|event| { /// events_loop.run_forever(move |event, _: &EventLoop| {
/// match event { /// match event {
/// Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { /// Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
/// ControlFlow::Break /// 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<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. /// Returned by the user callback given to the `EventLoop::run_forever` method.
/// ///
/// Indicates whether the `run_forever` method should continue or complete. /// Indicates whether the `run_forever` method should continue or complete.
@@ -240,19 +252,13 @@ impl EventLoop {
MonitorId { inner: self.events_loop.get_primary_monitor() } MonitorId { inner: self.events_loop.get_primary_monitor() }
} }
/// Calls `callback` every time an event is received. If no event is available, sleeps the /// Hijacks the calling thread and initializes the `winit` event loop. Can take a
/// current thread and waits for an event. If the callback returns `ControlFlow::Break` then /// `FnMut(Event, &EventLoop) -> ControlFlow` or a custom `EventHandler` type.
/// `run_forever` will immediately return.
/// ///
/// # Danger! /// Any values not passed to this function will *not* be dropped.
///
/// 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.
#[inline] #[inline]
pub fn run_forever<F>(&mut self, callback: F) pub fn run_forever(self, event_handler: impl 'static + EventHandler) -> ! {
where F: FnMut(Event) -> ControlFlow self.events_loop.run_forever(event_handler)
{
self.events_loop.run_forever(callback)
} }
/// Creates an `EventLoopProxy` that can be used to wake up the `EventLoop` from another /// Creates an `EventLoopProxy` that can be used to wake up the `EventLoop` from another

View File

@@ -5,6 +5,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::{mem, ptr}; use std::{mem, ptr};
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque;
use winapi::ctypes::c_void; use winapi::ctypes::c_void;
use winapi::shared::guiddef::REFIID; use winapi::shared::guiddef::REFIID;
@@ -25,7 +26,7 @@ pub struct FileDropHandlerData {
pub interface: IDropTarget, pub interface: IDropTarget,
refcount: AtomicUsize, refcount: AtomicUsize,
window: HWND, window: HWND,
event_queue: Rc<RefCell<Vec<Event>>>, event_queue: Rc<RefCell<VecDeque<Event>>>,
} }
pub struct FileDropHandler { pub struct FileDropHandler {
@@ -34,7 +35,7 @@ pub struct FileDropHandler {
#[allow(non_snake_case)] #[allow(non_snake_case)]
impl FileDropHandler { impl FileDropHandler {
pub fn new(window: HWND, event_queue: Rc<RefCell<Vec<Event>>>) -> FileDropHandler { pub fn new(window: HWND, event_queue: Rc<RefCell<VecDeque<Event>>>) -> FileDropHandler {
let data = Box::new(FileDropHandlerData { let data = Box::new(FileDropHandlerData {
interface: IDropTarget { interface: IDropTarget {
lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl, lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl,
@@ -187,7 +188,7 @@ impl FileDropHandler {
impl FileDropHandlerData { impl FileDropHandlerData {
fn send_event(&self, event: Event) { fn send_event(&self, event: Event) {
self.event_queue.borrow_mut().push(event); self.event_queue.borrow_mut().push_back(event);
} }
} }

View File

@@ -17,9 +17,8 @@ use winapi::shared::basetsd::UINT_PTR;
use std::rc::Rc; use std::rc::Rc;
use std::{mem, ptr}; use std::{mem, ptr};
use std::cell::RefCell; use std::cell::RefCell;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::collections::VecDeque;
use winapi::ctypes::c_int; use winapi::ctypes::c_int;
use winapi::shared::minwindef::{ use winapi::shared::minwindef::{
@@ -35,14 +34,13 @@ use winapi::shared::minwindef::{
}; };
use winapi::shared::windef::{HWND, POINT, RECT}; use winapi::shared::windef::{HWND, POINT, RECT};
use winapi::shared::windowsx; use winapi::shared::windowsx;
use winapi::shared::winerror::S_OK; use winapi::um::{winuser, processthreadsapi, ole2, commctrl};
use winapi::um::{winuser, processthreadsapi, ole2, shellapi, commctrl};
use winapi::um::oleidl::LPDROPTARGET;
use winapi::um::winnt::{LONG, LPCSTR, SHORT}; use winapi::um::winnt::{LONG, LPCSTR, SHORT};
use { use {
ControlFlow, ControlFlow,
Event, Event,
EventHandler,
EventLoopClosed, EventLoopClosed,
KeyboardInput, KeyboardInput,
LogicalPosition, LogicalPosition,
@@ -110,13 +108,13 @@ pub struct WindowState {
pub(crate) struct SubclassInput { pub(crate) struct SubclassInput {
pub window_state: Arc<Mutex<WindowState>>, pub window_state: Arc<Mutex<WindowState>>,
pub event_queue: Rc<RefCell<Vec<Event>>>, pub event_queue: Rc<RefCell<VecDeque<Event>>>,
pub file_drop_handler: FileDropHandler pub file_drop_handler: FileDropHandler
} }
impl SubclassInput { impl SubclassInput {
fn send_event(&self, event: Event) { 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 { pub struct EventLoop {
// Id of the background thread from the Win32 API. // Id of the background thread from the Win32 API.
thread_id: DWORD, thread_id: DWORD,
pub(crate) event_queue: Rc<RefCell<Vec<Event>>> pub(crate) event_queue: Rc<RefCell<VecDeque<Event>>>
} }
impl EventLoop { impl EventLoop {
@@ -151,13 +149,15 @@ impl EventLoop {
EventLoop { EventLoop {
thread_id, thread_id,
event_queue: Rc::new(RefCell::new(Vec::new())) event_queue: Rc::new(RefCell::new(VecDeque::new()))
} }
} }
pub fn run_forever<F>(&mut self, mut callback: F) pub fn run_forever(self, mut event_handler: impl 'static + EventHandler) -> ! {
where F: FnMut(Event) -> ControlFlow let event_loop = ::EventLoop {
{ events_loop: self,
_marker: ::std::marker::PhantomData
};
unsafe { unsafe {
// Calling `PostThreadMessageA` on a thread that does not have an events queue yet // 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 // will fail. In order to avoid this situation, we call `IsGuiThread` to initialize
@@ -166,17 +166,17 @@ impl EventLoop {
let mut msg = mem::uninitialized(); let mut msg = mem::uninitialized();
loop { 'main: loop {
if winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 { if winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
// Only happens if the message is `WM_QUIT`. // Only happens if the message is `WM_QUIT`.
debug_assert_eq!(msg.message, winuser::WM_QUIT); debug_assert_eq!(msg.message, winuser::WM_QUIT);
return; break 'main;
} }
match msg.message { match msg.message {
x if x == *WAKEUP_MSG_ID => { x if x == *WAKEUP_MSG_ID => {
if ControlFlow::Break == callback(Event::Awakened) { if ControlFlow::Break == event_handler.handle_event(Event::Awakened, &event_loop) {
return; break 'main;
} }
}, },
x if x == *EXEC_MSG_ID => { x if x == *EXEC_MSG_ID => {
@@ -184,20 +184,29 @@ impl EventLoop {
function() function()
} }
_ => { _ => {
// Calls `callback` below. // Calls `event_handler` below.
winuser::TranslateMessage(&msg); winuser::TranslateMessage(&msg);
winuser::DispatchMessageW(&msg); winuser::DispatchMessageW(&msg);
} }
} }
let mut event_queue = self.event_queue.borrow_mut(); loop {
for event in event_queue.drain(..) { // For whatever reason doing this in a `whlie let` loop doesn't drop the `RefMut`,
if ControlFlow::Break == callback(event) { // so we have to do it like this.
return; 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 { pub fn create_proxy(&self) -> EventLoopProxy {