From f2dd2f07524564ab42398a8fb6ee5d878548e1cb Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Fri, 2 Jun 2017 21:19:45 +1000 Subject: [PATCH] WIP - Make poll_events and run_forever take &mut self This removes the need for the EventsLoop::interrupt method by inroducing a ControlFlow type. This new type is to be returned by the user's callback and indicates whether the `EventsLoop` should continue waiting for events or break from the loop. Only the wayland, x11 and api_transition backends have been updated so far, and only the wayland backend has actually been tested. --- examples/window.rs | 6 +- src/api_transition.rs | 15 ++--- src/lib.rs | 29 +++++---- src/platform/linux/mod.rs | 28 ++++----- src/platform/linux/wayland/event_loop.rs | 39 ++++++------ src/platform/linux/x11/mod.rs | 79 +++++++++++++----------- src/window.rs | 2 +- 7 files changed, 103 insertions(+), 95 deletions(-) diff --git a/examples/window.rs b/examples/window.rs index c6d9327a..921b7ea6 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -12,8 +12,10 @@ fn main() { println!("{:?}", event); match event { - winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(), - _ => () + winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => { + winit::ControlFlow::Complete + }, + _ => winit::ControlFlow::Continue, } }); } diff --git a/src/api_transition.rs b/src/api_transition.rs index 7cd14a1b..db2c617d 100644 --- a/src/api_transition.rs +++ b/src/api_transition.rs @@ -8,7 +8,7 @@ macro_rules! gen_api_transition { () => { pub struct EventsLoop { - windows: ::std::sync::Mutex>>, + windows: ::std::sync::Arc<::std::sync::Mutex>>>, interrupted: ::std::sync::atomic::AtomicBool, awakened: ::std::sync::Arc<::std::sync::atomic::AtomicBool>, } @@ -20,7 +20,7 @@ macro_rules! gen_api_transition { impl EventsLoop { pub fn new() -> EventsLoop { EventsLoop { - windows: ::std::sync::Mutex::new(vec![]), + windows: ::std::sync::Arc::new(::std::sync::Mutex::new(vec![])), interrupted: ::std::sync::atomic::AtomicBool::new(false), awakened: ::std::sync::Arc::new(::std::sync::atomic::AtomicBool::new(false)), } @@ -92,7 +92,7 @@ macro_rules! gen_api_transition { pub struct Window2 { pub window: ::std::sync::Arc, - events_loop: ::std::sync::Weak, + windows: ::std::sync::Weak<::std::sync::Mutex>>> } impl ::std::ops::Deref for Window2 { @@ -104,7 +104,8 @@ macro_rules! gen_api_transition { } impl Window2 { - pub fn new(events_loop: ::std::sync::Arc, window: &::WindowAttributes, + pub fn new(events_loop: &EventsLoop, + window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result { @@ -112,7 +113,7 @@ macro_rules! gen_api_transition { events_loop.windows.lock().unwrap().push(win.clone()); Ok(Window2 { window: win, - events_loop: ::std::sync::Arc::downgrade(&events_loop), + events_loop: ::std::sync::Arc::downgrade(&events_loop.windows), }) } @@ -124,8 +125,8 @@ macro_rules! gen_api_transition { impl Drop for Window2 { fn drop(&mut self) { - if let Some(ev) = self.events_loop.upgrade() { - let mut windows = ev.windows.lock().unwrap(); + if let Some(windows) = self.windows.upgrade() { + let mut windows = windows.lock().unwrap(); windows.retain(|w| &**w as *const Window != &*self.window as *const _); } } diff --git a/src/lib.rs b/src/lib.rs index d583b267..4097b83a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,8 +119,6 @@ extern crate x11_dl; #[macro_use(wayland_env,declare_handler)] extern crate wayland_client; -use std::sync::Arc; - pub use events::*; pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor}; pub use native_monitor::NativeMonitorId; @@ -189,21 +187,32 @@ pub struct ButtonId(u32); /// /// To wake up an `EventsLoop` from a another thread, see the `EventsLoopProxy` docs. pub struct EventsLoop { - events_loop: Arc, + events_loop: platform::EventsLoop, +} + +/// Returned by the user callback given to the `EventsLoop::run_forever` method. +/// +/// Indicates whether the `run_forever` method should continue or complete. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ControlFlow { + /// Continue looping and waiting for events. + Continue, + /// Exit from the event loop. + Complete, } impl EventsLoop { /// Builds a new events loop. pub fn new() -> EventsLoop { EventsLoop { - events_loop: Arc::new(platform::EventsLoop::new()), + events_loop: platform::EventsLoop::new(), } } /// Fetches all the events that are pending, calls the callback function for each of them, /// and returns. #[inline] - pub fn poll_events(&self, callback: F) + pub fn poll_events(&mut self, callback: F) where F: FnMut(Event) { self.events_loop.poll_events(callback) @@ -211,18 +220,12 @@ impl EventsLoop { /// Runs forever until `interrupt()` is called. Whenever an event happens, calls the callback. #[inline] - pub fn run_forever(&self, callback: F) - where F: FnMut(Event) + pub fn run_forever(&mut self, callback: F) + where F: FnMut(Event) -> ControlFlow { self.events_loop.run_forever(callback) } - /// If we called `run_forever()`, stops the process of waiting for events. - #[inline] - pub fn interrupt(&self) { - self.events_loop.interrupt() - } - /// Creates an `EventsLoopProxy` that can be used to wake up the `EventsLoop` from another /// thread. pub fn create_proxy(&self) -> EventsLoopProxy { diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index fdc45d46..6bc4719d 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -3,7 +3,7 @@ use std::collections::VecDeque; use std::sync::Arc; -use {CreationError, CursorState, EventsLoopClosed, MouseCursor}; +use {CreationError, CursorState, EventsLoopClosed, MouseCursor, ControlFlow}; use libc; use self::x11::XConnection; @@ -129,9 +129,10 @@ impl MonitorId { impl Window2 { #[inline] - pub fn new(events_loop: ::std::sync::Arc, window: &::WindowAttributes, + pub fn new(events_loop: &EventsLoop, + window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) - -> Result + -> Result { match *UNIX_BACKEND { UnixBackend::Wayland(ref ctxt) => { @@ -336,28 +337,21 @@ impl EventsLoop { } } - pub fn interrupt(&self) { - match *self { - EventsLoop::Wayland(ref evlp) => evlp.interrupt(), - EventsLoop::X(ref evlp) => evlp.interrupt() - } - } - - pub fn poll_events(&self, callback: F) + pub fn poll_events(&mut self, callback: F) where F: FnMut(::Event) { match *self { - EventsLoop::Wayland(ref evlp) => evlp.poll_events(callback), - EventsLoop::X(ref evlp) => evlp.poll_events(callback) + EventsLoop::Wayland(ref mut evlp) => evlp.poll_events(callback), + EventsLoop::X(ref mut evlp) => evlp.poll_events(callback) } } - pub fn run_forever(&self, callback: F) - where F: FnMut(::Event) + pub fn run_forever(&mut self, callback: F) + where F: FnMut(::Event) -> ControlFlow { match *self { - EventsLoop::Wayland(ref evlp) => evlp.run_forever(callback), - EventsLoop::X(ref evlp) => evlp.run_forever(callback) + EventsLoop::Wayland(ref mut evlp) => evlp.run_forever(callback), + EventsLoop::X(ref mut evlp) => evlp.run_forever(callback) } } } diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index 17b136a3..f2b1325e 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -1,4 +1,5 @@ -use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState, KeyboardInput, EventsLoopClosed}; +use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState, + KeyboardInput, EventsLoopClosed, ControlFlow}; use std::sync::{Arc, Mutex, Weak}; use std::sync::atomic::{self, AtomicBool}; @@ -53,7 +54,9 @@ impl EventsLoopSink { ::std::mem::replace(&mut self.callback, cb) } - fn with_callback(&mut self, f: F) { + fn with_callback(&mut self, f: F) + where F: FnOnce(&mut FnMut(::Event)), + { f(&mut *self.callback) } } @@ -67,8 +70,6 @@ pub struct EventsLoop { decorated_ids: Mutex)>>, // our sink, receiver of callbacks, shared with some handlers sink: Arc>, - // trigger interruption of the run - interrupted: AtomicBool, // trigger cleanup of the dead surfaces cleanup_needed: Arc, // Whether or not there is a pending `Awakened` event to be emitted. @@ -115,7 +116,6 @@ impl EventsLoop { evq: Arc::new(Mutex::new(evq)), decorated_ids: Mutex::new(Vec::new()), sink: sink, - interrupted: AtomicBool::new(false), pending_wakeup: Arc::new(AtomicBool::new(false)), cleanup_needed: Arc::new(AtomicBool::new(false)), hid: hid @@ -158,10 +158,6 @@ impl EventsLoop { } } - pub fn interrupt(&self) { - self.interrupted.store(true, atomic::Ordering::Relaxed); - } - fn prune_dead_windows(&self) { self.decorated_ids.lock().unwrap().retain(|&(_, ref w)| w.is_alive()); let mut evq_guard = self.evq.lock().unwrap(); @@ -175,7 +171,7 @@ impl EventsLoop { } } - pub fn poll_events(&self, callback: F) + pub fn poll_events(&mut self, callback: F) where F: FnMut(::Event) { // send pending requests to the server... @@ -219,38 +215,45 @@ impl EventsLoop { } } - pub fn run_forever(&self, callback: F) - where F: FnMut(::Event) + pub fn run_forever(&mut self, mut callback: F) + where F: FnMut(::Event) -> ControlFlow, { - self.interrupted.store(false, atomic::Ordering::Relaxed); - // send pending requests to the server... self.ctxt.flush(); // first of all, get exclusive access to this event queue let mut evq_guard = self.evq.lock().unwrap(); + // Check for control flow by wrapping the callback. + let control_flow = ::std::cell::Cell::new(ControlFlow::Continue); + let callback = |event| if let ControlFlow::Complete = callback(event) { + control_flow.set(ControlFlow::Complete); + }; + // set the callback into the sink // we extend the lifetime of the closure to 'static to be able to put it in // the sink, but we'll explicitly drop it at the end of this function, so it's fine let static_cb = unsafe { ::std::mem::transmute(Box::new(callback) as Box) }; let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) }; - while !self.interrupted.load(atomic::Ordering::Relaxed) { + loop { self.ctxt.dispatch(); evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost"); self.emit_pending_wakeup(); let ids_guard = self.decorated_ids.lock().unwrap(); - self.sink.lock().unwrap().with_callback( - |cb| Self::process_resize(&mut evq_guard, &ids_guard, cb) - ); + self.sink.lock().unwrap() + .with_callback(|cb| Self::process_resize(&mut evq_guard, &ids_guard, cb)); self.ctxt.flush(); if self.cleanup_needed.swap(false, atomic::Ordering::Relaxed) { self.prune_dead_windows() } + + if let ControlFlow::Complete = control_flow.get() { + break; + } } // replace the old noop callback diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index eb98fb97..f18b986c 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -7,7 +7,8 @@ pub use self::xdisplay::{XConnection, XNotSupported, XError}; pub mod ffi; use platform::PlatformSpecificWindowBuilderAttributes; -use {CreationError, Event, EventsLoopClosed, WindowEvent, DeviceEvent, AxisId, ButtonId, KeyboardInput}; +use {CreationError, Event, EventsLoopClosed, WindowEvent, DeviceEvent, AxisId, ButtonId, + KeyboardInput, ControlFlow}; use std::{mem, ptr, slice}; use std::sync::{Arc, Mutex, Weak}; @@ -30,10 +31,9 @@ mod xdisplay; // the one generated by the macro. pub struct EventsLoop { - interrupted: AtomicBool, display: Arc, wm_delete_window: ffi::Atom, - windows: Mutex>, + windows: Arc>>, devices: Mutex>, xi2ext: XExtension, pending_wakeup: Arc, @@ -81,11 +81,10 @@ impl EventsLoop { let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) }; let result = EventsLoop { - interrupted: AtomicBool::new(false), pending_wakeup: Arc::new(AtomicBool::new(false)), display: display, wm_delete_window: wm_delete_window, - windows: Mutex::new(HashMap::new()), + windows: Arc::new(Mutex::new(HashMap::new())), devices: Mutex::new(HashMap::new()), xi2ext: xi2ext, root: root, @@ -118,14 +117,9 @@ impl EventsLoop { } } - pub fn interrupt(&self) { - self.interrupted.store(true, atomic::Ordering::Relaxed); - } - pub fn poll_events(&self, mut callback: F) where F: FnMut(Event) { - self.interrupted.store(false, atomic::Ordering::Relaxed); let xlib = &self.display.xlib; let mut xev = unsafe { mem::uninitialized() }; @@ -141,16 +135,12 @@ impl EventsLoop { (xlib.XNextEvent)(self.display.display, &mut xev); } self.process_event(&mut xev, &mut callback); - if self.interrupted.load(atomic::Ordering::Relaxed) { - break; - } } } - pub fn run_forever(&self, mut callback: F) - where F: FnMut(Event) + pub fn run_forever(&mut self, mut callback: F) + where F: FnMut(Event) -> ControlFlow { - self.interrupted.store(false, atomic::Ordering::Relaxed); self.pending_wakeup.store(false, atomic::Ordering::Relaxed); let xlib = &self.display.xlib; @@ -160,13 +150,25 @@ impl EventsLoop { loop { unsafe { (xlib.XNextEvent)(self.display.display, &mut xev) }; // Blocks as necessary + let mut control_flow = ControlFlow::Continue; + if self.pending_wakeup.load(atomic::Ordering::Relaxed) { self.pending_wakeup.store(false, atomic::Ordering::Relaxed); - callback(Event::Awakened); + control_flow = callback(Event::Awakened); } - self.process_event(&mut xev, &mut callback); - if self.interrupted.load(atomic::Ordering::Relaxed) { + // Track whether or not `Complete` was returned when processing the event. + { + let mut cb = |event| { + if let ControlFlow::Complete = callback(event) { + control_flow = ControlFlow::Complete; + } + }; + + self.process_event(&mut xev, &mut cb); + } + + if let ControlFlow::Complete = control_flow { break; } } @@ -178,7 +180,7 @@ impl EventsLoop { device.name.clone() } - fn process_event(&self, xev: &mut ffi::XEvent, callback: &mut F) + fn process_event(&self, xev: &mut ffi::XEvent, mut callback: F) where F: FnMut(Event) { let xlib = &self.display.xlib; @@ -312,7 +314,11 @@ impl EventsLoop { }; for chr in written.chars() { - callback(Event::WindowEvent { window_id: wid, event: WindowEvent::ReceivedCharacter(chr) }) + let event = Event::WindowEvent { + window_id: wid, + event: WindowEvent::ReceivedCharacter(chr), + }; + callback(event); } } } @@ -603,7 +609,8 @@ pub struct DeviceId(c_int); pub struct Window2 { pub window: Arc, - events_loop: Weak<::platform::EventsLoop>, + display: Weak, + windows: Weak>>, } impl ::std::ops::Deref for Window2 { @@ -620,9 +627,10 @@ lazy_static! { // TODO: use a static mutex when that's possible, and put me } impl Window2 { - pub fn new(events_loop: Arc<::platform::EventsLoop>, - window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) - -> Result + pub fn new(events_loop: &::platform::EventsLoop, + window: &::WindowAttributes, + pl_attribs: &PlatformSpecificWindowBuilderAttributes) + -> Result { let x_events_loop = if let ::platform::EventsLoop::X(ref e) = *events_loop { e } else { unreachable!() }; let win = ::std::sync::Arc::new(try!(Window::new(&x_events_loop, window, pl_attribs))); @@ -662,7 +670,8 @@ impl Window2 { Ok(Window2 { window: win, - events_loop: Arc::downgrade(&events_loop), + windows: Arc::downgrade(&x_events_loop.windows), + display: Arc::downgrade(&x_events_loop.display), }) } @@ -674,17 +683,13 @@ impl Window2 { impl Drop for Window2 { fn drop(&mut self) { - if let Some(ev) = self.events_loop.upgrade() { - if let ::platform::EventsLoop::X(ref ev) = *ev { - let mut windows = ev.windows.lock().unwrap(); - - - let w = windows.remove(&self.window.id()).unwrap(); - let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap(); - unsafe { - (ev.display.xlib.XDestroyIC)(w.ic); - (ev.display.xlib.XCloseIM)(w.im); - } + if let (Some(windows), Some(display)) = (self.windows.upgrade(), self.display.upgrade()) { + let mut windows = windows.lock().unwrap(); + let w = windows.remove(&self.window.id()).unwrap(); + let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap(); + unsafe { + (display.xlib.XDestroyIC)(w.ic); + (display.xlib.XCloseIM)(w.im); } } } diff --git a/src/window.rs b/src/window.rs index 349c31b1..94c677b0 100644 --- a/src/window.rs +++ b/src/window.rs @@ -110,7 +110,7 @@ impl WindowBuilder { } // building - let w = try!(platform::Window2::new(events_loop.events_loop.clone(), &self.window, &self.platform_specific)); + let w = try!(platform::Window2::new(&events_loop.events_loop, &self.window, &self.platform_specific)); Ok(Window { window: w }) }