diff --git a/README.md b/README.md index 7979037a..868486de 100644 --- a/README.md +++ b/README.md @@ -28,15 +28,15 @@ another library. extern crate winit; fn main() { - let events_loop = winit::EventsLoop::new(); + let mut events_loop = winit::EventsLoop::new(); let window = winit::Window::new(&events_loop).unwrap(); events_loop.run_forever(|event| { match event { winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => { - events_loop.interrupt(); + winit::ControlFlow::Break }, - _ => () + _ => winit::ControlFlow::Continue, } }); } diff --git a/examples/cursor.rs b/examples/cursor.rs index dcb37971..bc7deea6 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -1,9 +1,9 @@ extern crate winit; -use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput}; +use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow}; fn main() { - let events_loop = winit::EventsLoop::new(); + let mut events_loop = winit::EventsLoop::new(); let window = winit::WindowBuilder::new().build(&events_loop).unwrap(); window.set_title("A fantastic window!"); @@ -23,9 +23,10 @@ fn main() { } }, Event::WindowEvent { event: WindowEvent::Closed, .. } => { - events_loop.interrupt() + return ControlFlow::Break; }, _ => () } + ControlFlow::Continue }); } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 1c1d8bdb..6f2f118a 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -1,6 +1,7 @@ extern crate winit; use std::io::{self, Write}; +use winit::{ControlFlow, Event, WindowEvent}; fn main() { // enumerating monitors @@ -22,7 +23,7 @@ fn main() { monitor }; - let events_loop = winit::EventsLoop::new(); + let mut events_loop = winit::EventsLoop::new(); let _window = winit::WindowBuilder::new() .with_title("Hello world!") @@ -34,16 +35,18 @@ fn main() { println!("{:?}", event); match event { - winit::Event::WindowEvent { event, .. } => { + Event::WindowEvent { event, .. } => { match event { - winit::WindowEvent::Closed => events_loop.interrupt(), - winit::WindowEvent::KeyboardInput { + WindowEvent::Closed => return ControlFlow::Break, + WindowEvent::KeyboardInput { input: winit::KeyboardInput { virtual_keycode: Some(winit::VirtualKeyCode::Escape), .. }, .. - } => events_loop.interrupt(), + } => return ControlFlow::Break, _ => () } }, _ => {} } + + ControlFlow::Continue }); } diff --git a/examples/grabbing.rs b/examples/grabbing.rs index 2ba327f4..f7e7c9b3 100644 --- a/examples/grabbing.rs +++ b/examples/grabbing.rs @@ -1,9 +1,9 @@ extern crate winit; -use winit::{WindowEvent, ElementState, KeyboardInput}; +use winit::{ControlFlow, WindowEvent, ElementState, KeyboardInput}; fn main() { - let events_loop = winit::EventsLoop::new(); + let mut events_loop = winit::EventsLoop::new(); let window = winit::WindowBuilder::new().build(&events_loop).unwrap(); window.set_title("winit - Cursor grabbing test"); @@ -28,7 +28,7 @@ fn main() { } }, - WindowEvent::Closed => events_loop.interrupt(), + WindowEvent::Closed => return ControlFlow::Break, a @ WindowEvent::MouseMoved { .. } => { println!("{:?}", a); @@ -39,5 +39,7 @@ fn main() { } _ => {} } + + ControlFlow::Continue }); } diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index fe22f3e9..7500e893 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -1,7 +1,7 @@ extern crate winit; fn main() { - let events_loop = winit::EventsLoop::new(); + let mut events_loop = winit::EventsLoop::new(); let _window = winit::WindowBuilder::new() .with_min_dimensions(400, 200) @@ -13,8 +13,8 @@ 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::Break, + _ => winit::ControlFlow::Continue, } }); } diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 6c92b69e..9c5b93b8 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -1,7 +1,7 @@ extern crate winit; fn main() { - let events_loop = winit::EventsLoop::new(); + let mut events_loop = winit::EventsLoop::new(); let window1 = winit::Window::new(&events_loop).unwrap(); let window2 = winit::Window::new(&events_loop).unwrap(); @@ -24,10 +24,11 @@ fn main() { num_windows -= 1; if num_windows == 0 { - events_loop.interrupt(); + return winit::ControlFlow::Break; } }, _ => (), } + winit::ControlFlow::Continue }) } diff --git a/examples/proxy.rs b/examples/proxy.rs new file mode 100644 index 00000000..338c4675 --- /dev/null +++ b/examples/proxy.rs @@ -0,0 +1,29 @@ +extern crate winit; + +fn main() { + let mut events_loop = winit::EventsLoop::new(); + + let _window = winit::WindowBuilder::new() + .with_title("A fantastic window!") + .build(&events_loop) + .unwrap(); + + let proxy = events_loop.create_proxy(); + + std::thread::spawn(move || { + // Wake up the `events_loop` once every second. + loop { + std::thread::sleep(std::time::Duration::from_secs(1)); + proxy.wakeup().unwrap(); + } + }); + + events_loop.run_forever(|event| { + println!("{:?}", event); + match event { + winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => + winit::ControlFlow::Break, + _ => winit::ControlFlow::Continue, + } + }); +} diff --git a/examples/transparent.rs b/examples/transparent.rs index 9e476dee..de633175 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -1,7 +1,7 @@ extern crate winit; fn main() { - let events_loop = winit::EventsLoop::new(); + let mut events_loop = winit::EventsLoop::new(); let window = winit::WindowBuilder::new().with_decorations(false) .with_transparency(true) @@ -13,8 +13,8 @@ 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::Break, + _ => winit::ControlFlow::Continue, } }); } diff --git a/examples/window.rs b/examples/window.rs index c6d9327a..65f0ade2 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -1,9 +1,9 @@ extern crate winit; fn main() { - let events_loop = winit::EventsLoop::new(); + let mut events_loop = winit::EventsLoop::new(); - let window = winit::WindowBuilder::new() + let _window = winit::WindowBuilder::new() .with_title("A fantastic window!") .build(&events_loop) .unwrap(); @@ -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::Break + }, + _ => winit::ControlFlow::Continue, } }); } diff --git a/src/api_transition.rs b/src/api_transition.rs index 56a25934..158c6d44 100644 --- a/src/api_transition.rs +++ b/src/api_transition.rs @@ -8,26 +8,31 @@ macro_rules! gen_api_transition { () => { pub struct EventsLoop { - windows: ::std::sync::Mutex>>, - interrupted: ::std::sync::atomic::AtomicBool, + windows: ::std::sync::Arc<::std::sync::Mutex>>>, + awakened: ::std::sync::Arc<::std::sync::atomic::AtomicBool>, + } + + pub struct EventsLoopProxy { + awakened: ::std::sync::Weak<::std::sync::atomic::AtomicBool>, } impl EventsLoop { pub fn new() -> EventsLoop { EventsLoop { - windows: ::std::sync::Mutex::new(vec![]), - interrupted: ::std::sync::atomic::AtomicBool::new(false), + windows: ::std::sync::Arc::new(::std::sync::Mutex::new(vec![])), + awakened: ::std::sync::Arc::new(::std::sync::atomic::AtomicBool::new(false)), } } - pub fn interrupt(&self) { - self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed); - } - - pub fn poll_events(&self, mut callback: F) + pub fn poll_events(&mut self, mut callback: F) where F: FnMut(::Event) { - let mut windows = self.windows.lock().unwrap(); + if self.awakened.load(::std::sync::atomic::Ordering::Relaxed) { + self.awakened.store(false, ::std::sync::atomic::Ordering::Relaxed); + callback(::Event::Awakened); + } + + let windows = self.windows.lock().unwrap(); for window in windows.iter() { for event in window.poll_events() { callback(::Event::WindowEvent { @@ -38,18 +43,41 @@ macro_rules! gen_api_transition { } } - 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, ::std::sync::atomic::Ordering::Relaxed); + self.awakened.store(false, ::std::sync::atomic::Ordering::Relaxed); // Yeah that's a very bad implementation. loop { - self.poll_events(|e| callback(e)); - ::std::thread::sleep_ms(5); - if self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) { + let mut control_flow = ::ControlFlow::Continue; + self.poll_events(|e| { + if let ::ControlFlow::Break = callback(e) { + control_flow = ::ControlFlow::Break; + } + }); + if let ::ControlFlow::Break = control_flow { break; } + ::std::thread::sleep(::std::time::Duration::from_millis(5)); + } + } + + pub fn create_proxy(&self) -> EventsLoopProxy { + EventsLoopProxy { + awakened: ::std::sync::Arc::downgrade(&self.awakened), + } + } + } + + impl EventsLoopProxy { + pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> { + match self.awakened.upgrade() { + None => Err(::EventsLoopClosed), + Some(awakened) => { + awakened.store(true, ::std::sync::atomic::Ordering::Relaxed); + Ok(()) + }, } } } @@ -62,7 +90,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 { @@ -74,7 +102,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 { @@ -82,7 +111,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), + windows: ::std::sync::Arc::downgrade(&events_loop.windows), }) } @@ -94,8 +123,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/events.rs b/src/events.rs index ac5e345c..a508b589 100644 --- a/src/events.rs +++ b/src/events.rs @@ -11,12 +11,11 @@ pub enum Event { device_id: DeviceId, event: DeviceEvent, }, + Awakened, } #[derive(Clone, Debug)] pub enum WindowEvent { - // TODO: remove ; can break the lib internally so be careful - Awakened, /// The size of the window has changed. Resized(u32, u32), diff --git a/src/lib.rs b/src/lib.rs index 623f03c3..d4b91c40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,10 +34,9 @@ //! screen, such as video games. //! //! ```no_run -//! use winit::Event; -//! use winit::WindowEvent; +//! use winit::{Event, WindowEvent}; //! # use winit::EventsLoop; -//! # let events_loop = EventsLoop::new(); +//! # let mut events_loop = EventsLoop::new(); //! //! loop { //! events_loop.poll_events(|event| { @@ -52,21 +51,20 @@ //! ``` //! //! The second way is to call `events_loop.run_forever(...)`. As its name tells, it will run -//! forever unless it is stopped by calling `events_loop.interrupt()`. +//! forever unless it is stopped by returning `ControlFlow::Break`. //! //! ```no_run -//! use winit::Event; -//! use winit::WindowEvent; +//! use winit::{ControlFlow, Event, WindowEvent}; //! # use winit::EventsLoop; -//! # let events_loop = EventsLoop::new(); +//! # let mut events_loop = EventsLoop::new(); //! //! events_loop.run_forever(|event| { //! match event { //! Event::WindowEvent { event: WindowEvent::Closed, .. } => { //! println!("The window was closed ; stopping"); -//! events_loop.interrupt(); +//! ControlFlow::Break //! }, -//! _ => () +//! _ => ControlFlow::Continue, //! } //! }); //! ``` @@ -119,8 +117,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; @@ -139,20 +135,17 @@ pub mod os; /// # Example /// /// ```no_run -/// use winit::Event; -/// use winit::EventsLoop; -/// use winit::Window; -/// use winit::WindowEvent; +/// use winit::{Event, EventsLoop, Window, WindowEvent, ControlFlow}; /// -/// let events_loop = EventsLoop::new(); +/// let mut events_loop = EventsLoop::new(); /// let window = Window::new(&events_loop).unwrap(); /// /// events_loop.run_forever(|event| { /// match event { /// Event::WindowEvent { event: WindowEvent::Closed, .. } => { -/// events_loop.interrupt(); +/// ControlFlow::Break /// }, -/// _ => () +/// _ => ControlFlow::Continue, /// } /// }); /// ``` @@ -186,24 +179,35 @@ pub struct AxisId(u32); pub struct ButtonId(u32); /// Provides a way to retreive events from the windows that were registered to it. -// TODO: document usage in multiple threads -#[derive(Clone)] +/// +/// 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, + /// Break from the event loop. + Break, } 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,17 +215,51 @@ 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. - // TODO: what if we're waiting from multiple threads? - #[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 { + EventsLoopProxy { + events_loop_proxy: self.events_loop.create_proxy(), + } + } +} + +/// Used to wake up the `EventsLoop` from another thread. +pub struct EventsLoopProxy { + events_loop_proxy: platform::EventsLoopProxy, +} + +impl EventsLoopProxy { + /// Wake up the `EventsLoop` from which this proxy was created. + /// + /// This causes the `EventsLoop` to emit an `Awakened` event. + /// + /// Returns an `Err` if the associated `EventsLoop` no longer exists. + pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { + self.events_loop_proxy.wakeup() + } +} + +/// The error that is returned when an `EventsLoopProxy` attempts to wake up an `EventsLoop` that +/// no longer exists. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct EventsLoopClosed; + +impl std::fmt::Display for EventsLoopClosed { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", std::error::Error::description(self)) + } +} + +impl std::error::Error for EventsLoopClosed { + fn description(&self) -> &str { + "Tried to wake up a closed `EventsLoop`" } } diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 18924b87..6bc4719d 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -3,9 +3,7 @@ use std::collections::VecDeque; use std::sync::Arc; -use CreationError; -use CursorState; -use MouseCursor; +use {CreationError, CursorState, EventsLoopClosed, MouseCursor, ControlFlow}; use libc; use self::x11::XConnection; @@ -131,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) => { @@ -309,6 +308,11 @@ pub enum EventsLoop { X(x11::EventsLoop) } +pub enum EventsLoopProxy { + X(x11::EventsLoopProxy), + Wayland(wayland::EventsLoopProxy), +} + impl EventsLoop { pub fn new() -> EventsLoop { match *UNIX_BACKEND { @@ -326,28 +330,37 @@ impl EventsLoop { } } - pub fn interrupt(&self) { + pub fn create_proxy(&self) -> EventsLoopProxy { match *self { - EventsLoop::Wayland(ref evlp) => evlp.interrupt(), - EventsLoop::X(ref evlp) => evlp.interrupt() + EventsLoop::Wayland(ref evlp) => EventsLoopProxy::Wayland(evlp.create_proxy()), + EventsLoop::X(ref evlp) => EventsLoopProxy::X(evlp.create_proxy()), } } - 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) + } + } +} + +impl EventsLoopProxy { + pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { + match *self { + EventsLoopProxy::Wayland(ref proxy) => proxy.wakeup(), + EventsLoopProxy::X(ref proxy) => proxy.wakeup(), } } } diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index b86e4635..703562b6 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -1,7 +1,8 @@ -use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState, KeyboardInput}; +use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState, + KeyboardInput, EventsLoopClosed, ControlFlow}; -use std::sync::{Arc, Mutex}; -use std::sync::atomic::AtomicBool; +use std::sync::{Arc, Mutex, Weak}; +use std::sync::atomic::{self, AtomicBool}; use super::{DecoratedHandler, WindowId, DeviceId, WaylandContext}; @@ -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,11 +70,40 @@ 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, - hid: usize + // Whether or not there is a pending `Awakened` event to be emitted. + pending_wakeup: Arc, + hid: usize, +} + +// A handle that can be sent across threads and used to wake up the `EventsLoop`. +// +// We should only try and wake up the `EventsLoop` if it still exists, so we hold Weak ptrs. +pub struct EventsLoopProxy { + ctxt: Weak, + pending_wakeup: Weak, +} + +impl EventsLoopProxy { + // Causes the `EventsLoop` to stop blocking on `run_forever` and emit an `Awakened` event. + // + // Returns `Err` if the associated `EventsLoop` no longer exists. + pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { + let ctxt = self.ctxt.upgrade(); + let wakeup = self.pending_wakeup.upgrade(); + match (ctxt, wakeup) { + (Some(ctxt), Some(wakeup)) => { + // Update the `EventsLoop`'s `pending_wakeup` flag. + wakeup.store(true, atomic::Ordering::Relaxed); + // Cause the `EventsLoop` to break from `dispatch` if it is currently blocked. + ctxt.display.sync(); + ctxt.display.flush().ok(); + Ok(()) + }, + _ => Err(EventsLoopClosed), + } + } } impl EventsLoop { @@ -84,12 +116,19 @@ 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 } } + pub fn create_proxy(&self) -> EventsLoopProxy { + EventsLoopProxy { + ctxt: Arc::downgrade(&self.ctxt), + pending_wakeup: Arc::downgrade(&self.pending_wakeup), + } + } + // some internals that Window needs access to pub fn get_window_init(&self) -> (Arc>, Arc) { (self.evq.clone(), self.cleanup_needed.clone()) @@ -119,10 +158,6 @@ impl EventsLoop { } } - pub fn interrupt(&self) { - self.interrupted.store(true, ::std::sync::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(); @@ -136,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... @@ -160,6 +195,8 @@ impl EventsLoop { self.ctxt.dispatch_pending(); evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost"); + self.emit_pending_wakeup(); + { let mut sink_guard = self.sink.lock().unwrap(); @@ -173,45 +210,63 @@ impl EventsLoop { unsafe { sink_guard.set_callback(old_cb) }; } - if self.cleanup_needed.swap(false, ::std::sync::atomic::Ordering::Relaxed) { + if self.cleanup_needed.swap(false, atomic::Ordering::Relaxed) { self.prune_dead_windows() } } - 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, ::std::sync::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::Break = callback(event) { + control_flow.set(ControlFlow::Break); + }; + // 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(::std::sync::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, ::std::sync::atomic::Ordering::Relaxed) { + if self.cleanup_needed.swap(false, atomic::Ordering::Relaxed) { self.prune_dead_windows() } + + if let ControlFlow::Break = control_flow.get() { + break; + } } // replace the old noop callback unsafe { self.sink.lock().unwrap().set_callback(old_cb) }; } + + // If an `EventsLoopProxy` has signalled a wakeup, emit an event and reset the flag. + fn emit_pending_wakeup(&self) { + if self.pending_wakeup.load(atomic::Ordering::Relaxed) { + self.sink.lock().unwrap().with_callback(|cb| cb(::Event::Awakened)); + self.pending_wakeup.store(false, atomic::Ordering::Relaxed); + } + } } enum KbdType { diff --git a/src/platform/linux/wayland/mod.rs b/src/platform/linux/wayland/mod.rs index f267669b..46665ee3 100644 --- a/src/platform/linux/wayland/mod.rs +++ b/src/platform/linux/wayland/mod.rs @@ -1,7 +1,7 @@ #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] pub use self::window::{Window, WindowId}; -pub use self::event_loop::EventsLoop; +pub use self::event_loop::{EventsLoop, EventsLoopProxy}; pub use self::context::{WaylandContext, MonitorId, get_available_monitors, get_primary_monitor}; diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index 7535e42e..7b8f6bd7 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -7,10 +7,12 @@ pub use self::xdisplay::{XConnection, XNotSupported, XError}; pub mod ffi; use platform::PlatformSpecificWindowBuilderAttributes; -use {CreationError, Event, 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}; +use std::sync::atomic::{self, AtomicBool}; use std::collections::HashMap; use std::ffi::CStr; @@ -29,13 +31,22 @@ mod xdisplay; // the one generated by the macro. pub struct EventsLoop { - interrupted: ::std::sync::atomic::AtomicBool, display: Arc, wm_delete_window: ffi::Atom, - windows: Mutex>, + windows: Arc>>, devices: Mutex>, xi2ext: XExtension, + pending_wakeup: Arc, root: ffi::Window, + // A dummy, `InputOnly` window that we can use to receive wakeup events and interrupt blocking + // `XNextEvent` calls. + wakeup_dummy_window: ffi::Window, +} + +pub struct EventsLoopProxy { + pending_wakeup: Weak, + display: Weak, + wakeup_dummy_window: ffi::Window, } impl EventsLoop { @@ -72,14 +83,22 @@ impl EventsLoop { let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) }; + let wakeup_dummy_window = unsafe { + let (x, y, w, h) = (10, 10, 10, 10); + let (border_w, border_px, background_px) = (0, 0, 0); + (display.xlib.XCreateSimpleWindow)(display.display, root, x, y, w, h, + border_w, border_px, background_px) + }; + let result = EventsLoop { - interrupted: ::std::sync::atomic::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, + wakeup_dummy_window: wakeup_dummy_window, }; { @@ -101,32 +120,17 @@ impl EventsLoop { result } - pub fn interrupt(&self) { - self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed); - - // Push an event on the X event queue so that methods like run_forever will advance. - let mut xev = ffi::XClientMessageEvent { - type_: ffi::ClientMessage, - window: self.root, - format: 32, - message_type: 0, - serial: 0, - send_event: 0, - display: self.display.display, - data: unsafe { mem::zeroed() }, - }; - - unsafe { - (self.display.xlib.XSendEvent)(self.display.display, self.root, 0, 0, mem::transmute(&mut xev)); - (self.display.xlib.XFlush)(self.display.display); - self.display.check_errors().expect("Failed to call XSendEvent after wakeup"); + pub fn create_proxy(&self) -> EventsLoopProxy { + EventsLoopProxy { + pending_wakeup: Arc::downgrade(&self.pending_wakeup), + display: Arc::downgrade(&self.display), + wakeup_dummy_window: self.wakeup_dummy_window, } } - pub fn poll_events(&self, mut callback: F) + pub fn poll_events(&mut self, mut callback: F) where F: FnMut(Event) { - self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed); let xlib = &self.display.xlib; let mut xev = unsafe { mem::uninitialized() }; @@ -142,16 +146,13 @@ impl EventsLoop { (xlib.XNextEvent)(self.display.display, &mut xev); } self.process_event(&mut xev, &mut callback); - if self.interrupted.load(::std::sync::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, ::std::sync::atomic::Ordering::Relaxed); + self.pending_wakeup.store(false, atomic::Ordering::Relaxed); let xlib = &self.display.xlib; @@ -159,8 +160,21 @@ impl EventsLoop { loop { unsafe { (xlib.XNextEvent)(self.display.display, &mut xev) }; // Blocks as necessary - self.process_event(&mut xev, &mut callback); - if self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) { + + let mut control_flow = ControlFlow::Continue; + + // Track whether or not `Break` was returned when processing the event. + { + let mut cb = |event| { + if let ControlFlow::Break = callback(event) { + control_flow = ControlFlow::Break; + } + }; + + self.process_event(&mut xev, &mut cb); + } + + if let ControlFlow::Break = control_flow { break; } } @@ -172,7 +186,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; @@ -196,8 +210,10 @@ impl EventsLoop { if client_msg.data.get_long(0) as ffi::Atom == self.wm_delete_window { callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Closed }) } else { - // FIXME: Prone to spurious wakeups - callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Awakened }) + if self.pending_wakeup.load(atomic::Ordering::Relaxed) { + self.pending_wakeup.store(false, atomic::Ordering::Relaxed); + callback(Event::Awakened); + } } } @@ -306,7 +322,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); } } } @@ -523,6 +543,46 @@ impl EventsLoop { } } +impl EventsLoopProxy { + pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { + // Update the `EventsLoop`'s `pending_wakeup` flag. + let display = match (self.pending_wakeup.upgrade(), self.display.upgrade()) { + (Some(wakeup), Some(display)) => { + wakeup.store(true, atomic::Ordering::Relaxed); + display + }, + _ => return Err(EventsLoopClosed), + }; + + // Push an event on the X event queue so that methods run_forever will advance. + // + // NOTE: This code (and the following `XSendEvent` code) is taken from the old + // `WindowProxy::wakeup` implementation. The code assumes that X11 is thread safe. Is this + // true? + let mut xev = ffi::XClientMessageEvent { + type_: ffi::ClientMessage, + window: self.wakeup_dummy_window, + format: 32, + message_type: 0, + serial: 0, + send_event: 0, + display: display.display, + data: unsafe { mem::zeroed() }, + }; + + unsafe { + let propagate = false as i32; + let event_mask = 0; + let xevent = &mut xev as *mut ffi::XClientMessageEvent as *mut ffi::XEvent; + (display.xlib.XSendEvent)(display.display, self.wakeup_dummy_window, propagate, event_mask, xevent); + (display.xlib.XFlush)(display.display); + display.check_errors().expect("Failed to call XSendEvent after wakeup"); + } + + Ok(()) + } +} + struct DeviceInfo<'a> { display: &'a XConnection, info: *const ffi::XIDeviceInfo, @@ -564,7 +624,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 { @@ -581,9 +642,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))); @@ -623,7 +685,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), }) } @@ -635,17 +698,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/platform/linux/x11/window.rs b/src/platform/linux/x11/window.rs index 475cab8a..f5fa6794 100644 --- a/src/platform/linux/x11/window.rs +++ b/src/platform/linux/x11/window.rs @@ -166,8 +166,7 @@ impl Window { }; // getting the root window - let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) }; - display.check_errors().expect("Failed to get root window"); + let root = ctx.root; // creating let mut set_win_attr = { diff --git a/src/platform/macos/events_loop.rs b/src/platform/macos/events_loop.rs index 5b075a69..52130bbe 100644 --- a/src/platform/macos/events_loop.rs +++ b/src/platform/macos/events_loop.rs @@ -1,17 +1,23 @@ +use {ControlFlow, EventsLoopClosed}; use cocoa::{self, appkit, foundation}; use cocoa::appkit::{NSApplication, NSEvent, NSView, NSWindow}; use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, ModifiersState, KeyboardInput}; +use std::collections::VecDeque; +use std::sync::{Arc, Mutex, Weak}; use super::window::Window; use std; use super::DeviceId; pub struct EventsLoop { - pub windows: std::sync::Mutex>>, - pub pending_events: std::sync::Mutex>, - modifiers: std::sync::Mutex, - interrupted: std::sync::atomic::AtomicBool, + modifiers: Modifiers, + pub shared: Arc, +} +// State shared between the `EventsLoop` and its registered windows. +pub struct Shared { + pub windows: Mutex>>, + pub pending_events: Mutex>, // The user event callback given via either of the `poll_events` or `run_forever` methods. // // We store the user's callback here so that it may be accessed by each of the window delegate @@ -23,6 +29,8 @@ pub struct EventsLoop { user_callback: UserCallback, } +pub struct Proxy {} + struct Modifiers { shift_pressed: bool, ctrl_pressed: bool, @@ -34,14 +42,76 @@ struct Modifiers { // // - ensure the callback pointer is never accidentally cloned // - ensure that only the `EventsLoop` can `store` and `drop` the callback pointer -// - `unsafe impl Send` and `Sync` so that `Send` and `Sync` can be implemented for `EventsLoop`. +// - Share access to the user callback with the NSWindow callbacks. pub struct UserCallback { - mutex: std::sync::Mutex>, + mutex: Mutex>, } -unsafe impl Send for UserCallback {} -unsafe impl Sync for UserCallback {} +impl Shared { + + pub fn new() -> Self { + Shared { + windows: Mutex::new(Vec::new()), + pending_events: Mutex::new(VecDeque::new()), + user_callback: UserCallback { mutex: Mutex::new(None) }, + } + } + + fn call_user_callback_with_pending_events(&self) { + loop { + let event = match self.pending_events.lock().unwrap().pop_front() { + Some(event) => event, + None => return, + }; + unsafe { + self.user_callback.call_with_event(event); + } + } + } + + // Calls the user callback if one exists. + // + // Otherwise, stores the event in the `pending_events` queue. + // + // This is necessary for the case when `WindowDelegate` callbacks are triggered during a call + // to the user's callback. + pub fn call_user_callback_with_event_or_store_in_pending(&self, event: Event) { + if self.user_callback.mutex.lock().unwrap().is_some() { + unsafe { + self.user_callback.call_with_event(event); + } + } else { + self.pending_events.lock().unwrap().push_back(event); + } + } + + // Removes the window with the given `Id` from the `windows` list. + // + // This is called when a window is either `Closed` or `Drop`ped. + pub fn find_and_remove_window(&self, id: super::window::Id) { + if let Ok(mut windows) = self.windows.lock() { + windows.retain(|w| match w.upgrade() { + Some(w) => w.id() != id, + None => true, + }); + } + } + +} + + +impl Modifiers { + pub fn new() -> Self { + Modifiers { + shift_pressed: false, + ctrl_pressed: false, + win_pressed: false, + alt_pressed: false, + } + } +} + impl UserCallback { @@ -92,22 +162,13 @@ impl UserCallback { impl EventsLoop { pub fn new() -> Self { - let modifiers = Modifiers { - shift_pressed: false, - ctrl_pressed: false, - win_pressed: false, - alt_pressed: false, - }; EventsLoop { - windows: std::sync::Mutex::new(Vec::new()), - pending_events: std::sync::Mutex::new(std::collections::VecDeque::new()), - modifiers: std::sync::Mutex::new(modifiers), - interrupted: std::sync::atomic::AtomicBool::new(false), - user_callback: UserCallback { mutex: std::sync::Mutex::new(None) }, + shared: Arc::new(Shared::new()), + modifiers: Modifiers::new(), } } - pub fn poll_events(&self, mut callback: F) + pub fn poll_events(&mut self, mut callback: F) where F: FnMut(Event), { unsafe { @@ -116,13 +177,13 @@ impl EventsLoop { } } - self.user_callback.store(&mut callback); + self.shared.user_callback.store(&mut callback); // Loop as long as we have pending events to return. loop { unsafe { // First, yield all pending events. - self.call_user_callback_with_pending_events(); + self.shared.call_user_callback_with_pending_events(); let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil); @@ -139,32 +200,42 @@ impl EventsLoop { match event { // Call the user's callback. - Some(event) => self.user_callback.call_with_event(event), + Some(event) => self.shared.user_callback.call_with_event(event), None => break, } } } - self.user_callback.drop(); + self.shared.user_callback.drop(); } - 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, std::sync::atomic::Ordering::Relaxed); - unsafe { if !msg_send![cocoa::base::class("NSThread"), isMainThread] { panic!("Events can only be polled from the main thread on macOS"); } } - self.user_callback.store(&mut callback); + // Track whether or not control flow has changed. + let control_flow = std::cell::Cell::new(ControlFlow::Continue); + + let mut callback = |event| { + if let ControlFlow::Break = callback(event) { + control_flow.set(ControlFlow::Break); + } + }; + + self.shared.user_callback.store(&mut callback); loop { unsafe { // First, yield all pending events. - self.call_user_callback_with_pending_events(); + self.shared.call_user_callback_with_pending_events(); + if let ControlFlow::Break = control_flow.get() { + break; + } let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil); @@ -182,84 +253,19 @@ impl EventsLoop { let _: () = msg_send![pool, release]; if let Some(event) = maybe_event { - self.user_callback.call_with_event(event); + self.shared.user_callback.call_with_event(event); + if let ControlFlow::Break = control_flow.get() { + break; + } } } - - if self.interrupted.load(std::sync::atomic::Ordering::Relaxed) { - self.interrupted.store(false, std::sync::atomic::Ordering::Relaxed); - break; - } } - self.user_callback.drop(); - } - - pub fn interrupt(&self) { - self.interrupted.store(true, std::sync::atomic::Ordering::Relaxed); - - // Awaken the event loop by triggering `NSApplicationActivatedEventType`. - unsafe { - let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil); - let event = - NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_( - cocoa::base::nil, - appkit::NSApplicationDefined, - foundation::NSPoint::new(0.0, 0.0), - appkit::NSEventModifierFlags::empty(), - 0.0, - 0, - cocoa::base::nil, - appkit::NSEventSubtype::NSApplicationActivatedEventType, - 0, - 0); - appkit::NSApp().postEvent_atStart_(event, cocoa::base::NO); - foundation::NSAutoreleasePool::drain(pool); - } - } - - // Removes the window with the given `Id` from the `windows` list. - // - // This is called when a window is either `Closed` or `Drop`ped. - pub fn find_and_remove_window(&self, id: super::window::Id) { - if let Ok(mut windows) = self.windows.lock() { - windows.retain(|w| match w.upgrade() { - Some(w) => w.id() != id, - None => true, - }); - } - } - - fn call_user_callback_with_pending_events(&self) { - loop { - let event = match self.pending_events.lock().unwrap().pop_front() { - Some(event) => event, - None => return, - }; - unsafe { - self.user_callback.call_with_event(event); - } - } - } - - // Calls the user callback if one exists. - // - // Otherwise, stores the event in the `pending_events` queue. - // - // This is necessary for the case when `WindowDelegate` callbacks are triggered during a call - // to the user's callback. - pub fn call_user_callback_with_event_or_store_in_pending(&self, event: Event) { - if self.user_callback.mutex.lock().unwrap().is_some() { - unsafe { - self.user_callback.call_with_event(event); - } - } else { - self.pending_events.lock().unwrap().push_back(event); - } + self.shared.user_callback.drop(); } // Convert some given `NSEvent` into a winit `Event`. - unsafe fn ns_event_to_event(&self, ns_event: cocoa::base::id) -> Option { + unsafe fn ns_event_to_event(&mut self, ns_event: cocoa::base::id) -> Option { if ns_event == cocoa::base::nil { return None; } @@ -285,9 +291,9 @@ impl EventsLoop { _ => appkit::NSApp().sendEvent_(ns_event), } - let windows = self.windows.lock().unwrap(); + let windows = self.shared.windows.lock().unwrap(); let maybe_window = windows.iter() - .filter_map(std::sync::Weak::upgrade) + .filter_map(Weak::upgrade) .find(|window| window_id == window.id()); let into_event = |window_event| Event::WindowEvent { @@ -297,7 +303,7 @@ impl EventsLoop { // Returns `Some` window if one of our windows is the key window. let maybe_key_window = || windows.iter() - .filter_map(std::sync::Weak::upgrade) + .filter_map(Weak::upgrade) .find(|window| { let is_key_window: cocoa::base::BOOL = msg_send![*window.window, isKeyWindow]; is_key_window == cocoa::base::YES @@ -326,7 +332,7 @@ impl EventsLoop { let window_event = WindowEvent::ReceivedCharacter(received_char); events.push_back(into_event(window_event)); } - self.pending_events.lock().unwrap().extend(events.into_iter()); + self.shared.pending_events.lock().unwrap().extend(events.into_iter()); Some(into_event(window_event)) }, @@ -348,8 +354,6 @@ impl EventsLoop { }, appkit::NSFlagsChanged => { - let mut modifiers = self.modifiers.lock().unwrap(); - unsafe fn modifier_event(event: cocoa::base::id, keymask: appkit::NSEventModifierFlags, key: events::VirtualKeyCode, @@ -392,41 +396,41 @@ impl EventsLoop { if let Some(window_event) = modifier_event(ns_event, appkit::NSShiftKeyMask, events::VirtualKeyCode::LShift, - modifiers.shift_pressed) + self.modifiers.shift_pressed) { - modifiers.shift_pressed = !modifiers.shift_pressed; + self.modifiers.shift_pressed = !self.modifiers.shift_pressed; events.push_back(into_event(window_event)); } if let Some(window_event) = modifier_event(ns_event, appkit::NSControlKeyMask, events::VirtualKeyCode::LControl, - modifiers.ctrl_pressed) + self.modifiers.ctrl_pressed) { - modifiers.ctrl_pressed = !modifiers.ctrl_pressed; + self.modifiers.ctrl_pressed = !self.modifiers.ctrl_pressed; events.push_back(into_event(window_event)); } if let Some(window_event) = modifier_event(ns_event, appkit::NSCommandKeyMask, events::VirtualKeyCode::LWin, - modifiers.win_pressed) + self.modifiers.win_pressed) { - modifiers.win_pressed = !modifiers.win_pressed; + self.modifiers.win_pressed = !self.modifiers.win_pressed; events.push_back(into_event(window_event)); } if let Some(window_event) = modifier_event(ns_event, appkit::NSAlternateKeyMask, events::VirtualKeyCode::LAlt, - modifiers.alt_pressed) + self.modifiers.alt_pressed) { - modifiers.alt_pressed = !modifiers.alt_pressed; + self.modifiers.alt_pressed = !self.modifiers.alt_pressed; events.push_back(into_event(window_event)); } let event = events.pop_front(); - self.pending_events.lock().unwrap().extend(events.into_iter()); + self.shared.pending_events.lock().unwrap().extend(events.into_iter()); event }, @@ -505,7 +509,7 @@ impl EventsLoop { appkit::NSApplicationDefined => match ns_event.subtype() { appkit::NSEventSubtype::NSApplicationActivatedEventType => { - Some(into_event(WindowEvent::Awakened)) + Some(Event::Awakened) }, _ => None, }, @@ -514,6 +518,34 @@ impl EventsLoop { } } + pub fn create_proxy(&self) -> Proxy { + Proxy {} + } + +} + +impl Proxy { + pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { + // Awaken the event loop by triggering `NSApplicationActivatedEventType`. + unsafe { + let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil); + let event = + NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_( + cocoa::base::nil, + appkit::NSApplicationDefined, + foundation::NSPoint::new(0.0, 0.0), + appkit::NSEventModifierFlags::empty(), + 0.0, + 0, + cocoa::base::nil, + appkit::NSEventSubtype::NSApplicationActivatedEventType, + 0, + 0); + appkit::NSApp().postEvent_atStart_(event, cocoa::base::NO); + foundation::NSAutoreleasePool::drain(pool); + } + Ok(()) + } } diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index 5b207bb2..dbde5ee9 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -1,8 +1,9 @@ #![cfg(target_os = "macos")] -pub use self::events_loop::EventsLoop; +pub use self::events_loop::{EventsLoop, Proxy as EventsLoopProxy}; pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor}; pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window}; +use std::sync::Arc; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DeviceId; @@ -10,7 +11,7 @@ pub struct DeviceId; use {CreationError}; pub struct Window2 { - pub window: ::std::sync::Arc, + pub window: Arc, } impl ::std::ops::Deref for Window2 { @@ -23,14 +24,14 @@ impl ::std::ops::Deref for Window2 { impl Window2 { - pub fn new(events_loop: ::std::sync::Arc, + pub fn new(events_loop: &EventsLoop, attributes: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result { - let weak_events_loop = ::std::sync::Arc::downgrade(&events_loop); - let window = ::std::sync::Arc::new(try!(Window::new(weak_events_loop, attributes, pl_attribs))); - let weak_window = ::std::sync::Arc::downgrade(&window); - events_loop.windows.lock().unwrap().push(weak_window); + let weak_shared = Arc::downgrade(&events_loop.shared); + let window = Arc::new(try!(Window::new(weak_shared, attributes, pl_attribs))); + let weak_window = Arc::downgrade(&window); + events_loop.shared.windows.lock().unwrap().push(weak_window); Ok(Window2 { window: window }) } diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index e4b1f1f4..8ea04a76 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -5,6 +5,7 @@ use libc; use WindowAttributes; use native_monitor::NativeMonitorId; use os::macos::ActivationPolicy; +use os::macos::WindowExt; use objc; use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; @@ -20,8 +21,9 @@ use core_graphics::display::{CGAssociateMouseAndMouseCursorPosition, CGMainDispl use std; use std::ops::Deref; use std::os::raw::c_void; +use std::sync::Weak; -use os::macos::WindowExt; +use super::events_loop::Shared; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -30,7 +32,7 @@ pub struct Id(pub usize); struct DelegateState { view: IdRef, window: IdRef, - events_loop: std::sync::Weak, + shared: Weak, } pub struct WindowDelegate { @@ -51,8 +53,8 @@ impl WindowDelegate { event: window_event, }; - if let Some(events_loop) = state.events_loop.upgrade() { - events_loop.call_user_callback_with_event_or_store_in_pending(event); + if let Some(shared) = state.shared.upgrade() { + shared.call_user_callback_with_event_or_store_in_pending(event); } } @@ -71,10 +73,10 @@ impl WindowDelegate { let state = &mut *(state as *mut DelegateState); emit_event(state, WindowEvent::Closed); - // Remove the window from the events_loop. - if let Some(events_loop) = state.events_loop.upgrade() { + // Remove the window from the shared state. + if let Some(shared) = state.shared.upgrade() { let window_id = get_window_id(*state.window); - events_loop.find_and_remove_window(window_id); + shared.find_and_remove_window(window_id); } } YES @@ -188,8 +190,8 @@ impl Drop for Window { fn drop(&mut self) { // Remove this window from the `EventLoop`s list of windows. let id = self.id(); - if let Some(ev) = self.delegate.state.events_loop.upgrade() { - ev.find_and_remove_window(id); + if let Some(shared) = self.delegate.state.shared.upgrade() { + shared.find_and_remove_window(id); } // Close the window if it has not yet been closed. @@ -215,7 +217,7 @@ impl WindowExt for Window { } impl Window { - pub fn new(events_loop: std::sync::Weak, + pub fn new(shared: Weak, win_attribs: &WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result @@ -266,7 +268,7 @@ impl Window { let ds = DelegateState { view: view.clone(), window: window.clone(), - events_loop: events_loop, + shared: shared, }; let window = Window { diff --git a/src/platform/windows/callback.rs b/src/platform/windows/callback.rs index 864cf9c9..c11aac13 100644 --- a/src/platform/windows/callback.rs +++ b/src/platform/windows/callback.rs @@ -410,8 +410,9 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, }, x if x == *super::WAKEUP_MSG_ID => { - use events::WindowEvent::Awakened; - send_event(window, Awakened); + // TODO: `Awakened` has been moved from the `WindowEvent` enum to the `Event` enum. + // This code needs to be updated to reflect this change. + //send_event(window, ::Event::Awakened); 0 }, 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 }) } diff --git a/tests/events_loop.rs b/tests/events_loop.rs deleted file mode 100644 index 85b3bb6b..00000000 --- a/tests/events_loop.rs +++ /dev/null @@ -1,10 +0,0 @@ -extern crate winit; - -// A part of the API requirement for `EventsLoop` is that it is `Send` + `Sync`. -// -// This short test will only compile if the `EventsLoop` is `Send` + `Sync`. -#[test] -fn send_sync() { - fn check_send_sync() {} - check_send_sync::(); -} diff --git a/tests/events_loop_proxy_send.rs b/tests/events_loop_proxy_send.rs new file mode 100644 index 00000000..5c44aa13 --- /dev/null +++ b/tests/events_loop_proxy_send.rs @@ -0,0 +1,8 @@ +extern crate winit; + +#[test] +fn events_loop_proxy_send() { + // ensures that `winit::EventsLoopProxy` implements `Send` + fn needs_send() {} + needs_send::(); +} diff --git a/tests/window_proxy_send.rs b/tests/window_proxy_send.rs deleted file mode 100644 index 7ad62641..00000000 --- a/tests/window_proxy_send.rs +++ /dev/null @@ -1,9 +0,0 @@ -extern crate winit; - -#[cfg(feature = "window")] -#[test] -fn window_proxy_send() { - // ensures that `winit::WindowProxy` implements `Send` - fn needs_send() {} - needs_send::(); -}