Merge pull request #191 from mitchmindtree/remove_sync

Remove Sync and Clone from EventsLoop. Add EventsLoopProxy.
This commit is contained in:
tomaka 2017-06-21 10:18:11 +02:00 committed by GitHub
commit a08347eef0
25 changed files with 586 additions and 331 deletions

View file

@ -28,15 +28,15 @@ another library.
extern crate winit; extern crate winit;
fn main() { fn main() {
let events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::new();
let window = winit::Window::new(&events_loop).unwrap(); let window = winit::Window::new(&events_loop).unwrap();
events_loop.run_forever(|event| { events_loop.run_forever(|event| {
match event { match event {
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => { winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => {
events_loop.interrupt(); winit::ControlFlow::Break
}, },
_ => () _ => winit::ControlFlow::Continue,
} }
}); });
} }

View file

@ -1,9 +1,9 @@
extern crate winit; extern crate winit;
use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput}; use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow};
fn main() { fn main() {
let events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::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!");
@ -23,9 +23,10 @@ fn main() {
} }
}, },
Event::WindowEvent { event: WindowEvent::Closed, .. } => { Event::WindowEvent { event: WindowEvent::Closed, .. } => {
events_loop.interrupt() return ControlFlow::Break;
}, },
_ => () _ => ()
} }
ControlFlow::Continue
}); });
} }

View file

@ -1,6 +1,7 @@
extern crate winit; extern crate winit;
use std::io::{self, Write}; use std::io::{self, Write};
use winit::{ControlFlow, Event, WindowEvent};
fn main() { fn main() {
// enumerating monitors // enumerating monitors
@ -22,7 +23,7 @@ fn main() {
monitor monitor
}; };
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("Hello world!") .with_title("Hello world!")
@ -34,16 +35,18 @@ fn main() {
println!("{:?}", event); println!("{:?}", event);
match event { match event {
winit::Event::WindowEvent { event, .. } => { Event::WindowEvent { event, .. } => {
match event { match event {
winit::WindowEvent::Closed => events_loop.interrupt(), WindowEvent::Closed => return ControlFlow::Break,
winit::WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: winit::KeyboardInput { virtual_keycode: Some(winit::VirtualKeyCode::Escape), .. }, .. input: winit::KeyboardInput { virtual_keycode: Some(winit::VirtualKeyCode::Escape), .. }, ..
} => events_loop.interrupt(), } => return ControlFlow::Break,
_ => () _ => ()
} }
}, },
_ => {} _ => {}
} }
ControlFlow::Continue
}); });
} }

View file

@ -1,9 +1,9 @@
extern crate winit; extern crate winit;
use winit::{WindowEvent, ElementState, KeyboardInput}; use winit::{ControlFlow, WindowEvent, ElementState, KeyboardInput};
fn main() { fn main() {
let events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::new();
let window = winit::WindowBuilder::new().build(&events_loop).unwrap(); let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
window.set_title("winit - Cursor grabbing test"); 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 { .. } => { a @ WindowEvent::MouseMoved { .. } => {
println!("{:?}", a); println!("{:?}", a);
@ -39,5 +39,7 @@ fn main() {
} }
_ => {} _ => {}
} }
ControlFlow::Continue
}); });
} }

View file

@ -1,7 +1,7 @@
extern crate winit; extern crate winit;
fn main() { 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_min_dimensions(400, 200) .with_min_dimensions(400, 200)
@ -13,8 +13,8 @@ fn main() {
println!("{:?}", event); println!("{:?}", event);
match 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,
} }
}); });
} }

View file

@ -1,7 +1,7 @@
extern crate winit; extern crate winit;
fn main() { fn main() {
let events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::new();
let window1 = winit::Window::new(&events_loop).unwrap(); let window1 = winit::Window::new(&events_loop).unwrap();
let window2 = winit::Window::new(&events_loop).unwrap(); let window2 = winit::Window::new(&events_loop).unwrap();
@ -24,10 +24,11 @@ fn main() {
num_windows -= 1; num_windows -= 1;
if num_windows == 0 { if num_windows == 0 {
events_loop.interrupt(); return winit::ControlFlow::Break;
} }
}, },
_ => (), _ => (),
} }
winit::ControlFlow::Continue
}) })
} }

29
examples/proxy.rs Normal file
View file

@ -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,
}
});
}

View file

@ -1,7 +1,7 @@
extern crate winit; extern crate winit;
fn main() { fn main() {
let events_loop = winit::EventsLoop::new(); let mut events_loop = winit::EventsLoop::new();
let window = winit::WindowBuilder::new().with_decorations(false) let window = winit::WindowBuilder::new().with_decorations(false)
.with_transparency(true) .with_transparency(true)
@ -13,8 +13,8 @@ fn main() {
println!("{:?}", event); println!("{:?}", event);
match 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,
} }
}); });
} }

View file

@ -1,9 +1,9 @@
extern crate winit; extern crate winit;
fn main() { 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!") .with_title("A fantastic window!")
.build(&events_loop) .build(&events_loop)
.unwrap(); .unwrap();
@ -12,8 +12,10 @@ fn main() {
println!("{:?}", event); println!("{:?}", event);
match 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,
} }
}); });
} }

View file

@ -8,26 +8,31 @@
macro_rules! gen_api_transition { macro_rules! gen_api_transition {
() => { () => {
pub struct EventsLoop { pub struct EventsLoop {
windows: ::std::sync::Mutex<Vec<::std::sync::Arc<Window>>>, windows: ::std::sync::Arc<::std::sync::Mutex<Vec<::std::sync::Arc<Window>>>>,
interrupted: ::std::sync::atomic::AtomicBool, awakened: ::std::sync::Arc<::std::sync::atomic::AtomicBool>,
}
pub struct EventsLoopProxy {
awakened: ::std::sync::Weak<::std::sync::atomic::AtomicBool>,
} }
impl EventsLoop { impl EventsLoop {
pub fn new() -> EventsLoop { pub fn new() -> EventsLoop {
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)),
} }
} }
pub fn interrupt(&self) { pub fn poll_events<F>(&mut self, mut callback: F)
self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed);
}
pub fn poll_events<F>(&self, mut callback: F)
where F: FnMut(::Event) 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 window in windows.iter() {
for event in window.poll_events() { for event in window.poll_events() {
callback(::Event::WindowEvent { callback(::Event::WindowEvent {
@ -38,18 +43,41 @@ macro_rules! gen_api_transition {
} }
} }
pub fn run_forever<F>(&self, mut callback: F) pub fn run_forever<F>(&mut self, mut callback: F)
where F: FnMut(::Event) 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. // Yeah that's a very bad implementation.
loop { loop {
self.poll_events(|e| callback(e)); let mut control_flow = ::ControlFlow::Continue;
::std::thread::sleep_ms(5); self.poll_events(|e| {
if self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) { if let ::ControlFlow::Break = callback(e) {
control_flow = ::ControlFlow::Break;
}
});
if let ::ControlFlow::Break = control_flow {
break; 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 struct Window2 {
pub window: ::std::sync::Arc<Window>, pub window: ::std::sync::Arc<Window>,
events_loop: ::std::sync::Weak<EventsLoop>, windows: ::std::sync::Weak<::std::sync::Mutex<Vec<::std::sync::Arc<Window>>>>
} }
impl ::std::ops::Deref for Window2 { impl ::std::ops::Deref for Window2 {
@ -74,7 +102,8 @@ macro_rules! gen_api_transition {
} }
impl Window2 { impl Window2 {
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>, window: &::WindowAttributes, pub fn new(events_loop: &EventsLoop,
window: &::WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes) pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window2, CreationError> -> Result<Window2, CreationError>
{ {
@ -82,7 +111,7 @@ macro_rules! gen_api_transition {
events_loop.windows.lock().unwrap().push(win.clone()); events_loop.windows.lock().unwrap().push(win.clone());
Ok(Window2 { Ok(Window2 {
window: win, 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 { impl Drop for Window2 {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(ev) = self.events_loop.upgrade() { if let Some(windows) = self.windows.upgrade() {
let mut windows = ev.windows.lock().unwrap(); let mut windows = windows.lock().unwrap();
windows.retain(|w| &**w as *const Window != &*self.window as *const _); windows.retain(|w| &**w as *const Window != &*self.window as *const _);
} }
} }

View file

@ -11,12 +11,11 @@ pub enum Event {
device_id: DeviceId, device_id: DeviceId,
event: DeviceEvent, event: DeviceEvent,
}, },
Awakened,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum WindowEvent { pub enum WindowEvent {
// TODO: remove ; can break the lib internally so be careful
Awakened,
/// The size of the window has changed. /// The size of the window has changed.
Resized(u32, u32), Resized(u32, u32),

View file

@ -34,10 +34,9 @@
//! screen, such as video games. //! screen, such as video games.
//! //!
//! ```no_run //! ```no_run
//! use winit::Event; //! use winit::{Event, WindowEvent};
//! use winit::WindowEvent;
//! # use winit::EventsLoop; //! # use winit::EventsLoop;
//! # let events_loop = EventsLoop::new(); //! # let mut events_loop = EventsLoop::new();
//! //!
//! loop { //! loop {
//! events_loop.poll_events(|event| { //! 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 //! 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 //! ```no_run
//! use winit::Event; //! use winit::{ControlFlow, Event, WindowEvent};
//! use winit::WindowEvent;
//! # use winit::EventsLoop; //! # use winit::EventsLoop;
//! # let events_loop = EventsLoop::new(); //! # let mut events_loop = EventsLoop::new();
//! //!
//! events_loop.run_forever(|event| { //! events_loop.run_forever(|event| {
//! match event { //! match event {
//! Event::WindowEvent { event: WindowEvent::Closed, .. } => { //! Event::WindowEvent { event: WindowEvent::Closed, .. } => {
//! println!("The window was closed ; stopping"); //! 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)] #[macro_use(wayland_env,declare_handler)]
extern crate wayland_client; extern crate wayland_client;
use std::sync::Arc;
pub use events::*; pub use events::*;
pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor}; pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor};
pub use native_monitor::NativeMonitorId; pub use native_monitor::NativeMonitorId;
@ -139,20 +135,17 @@ pub mod os;
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
/// use winit::Event; /// use winit::{Event, EventsLoop, Window, WindowEvent, ControlFlow};
/// use winit::EventsLoop;
/// use winit::Window;
/// use winit::WindowEvent;
/// ///
/// let events_loop = EventsLoop::new(); /// let mut events_loop = EventsLoop::new();
/// let window = Window::new(&events_loop).unwrap(); /// let window = Window::new(&events_loop).unwrap();
/// ///
/// events_loop.run_forever(|event| { /// events_loop.run_forever(|event| {
/// match event { /// match event {
/// Event::WindowEvent { event: WindowEvent::Closed, .. } => { /// Event::WindowEvent { event: WindowEvent::Closed, .. } => {
/// events_loop.interrupt(); /// ControlFlow::Break
/// }, /// },
/// _ => () /// _ => ControlFlow::Continue,
/// } /// }
/// }); /// });
/// ``` /// ```
@ -186,24 +179,35 @@ pub struct AxisId(u32);
pub struct ButtonId(u32); pub struct ButtonId(u32);
/// Provides a way to retreive events from the windows that were registered to it. /// 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 { pub struct EventsLoop {
events_loop: Arc<platform::EventsLoop>, 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 { impl EventsLoop {
/// Builds a new events loop. /// Builds a new events loop.
pub fn new() -> EventsLoop { pub fn new() -> EventsLoop {
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, /// Fetches all the events that are pending, calls the callback function for each of them,
/// and returns. /// and returns.
#[inline] #[inline]
pub fn poll_events<F>(&self, callback: F) pub fn poll_events<F>(&mut self, callback: F)
where F: FnMut(Event) where F: FnMut(Event)
{ {
self.events_loop.poll_events(callback) 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. /// Runs forever until `interrupt()` is called. Whenever an event happens, calls the callback.
#[inline] #[inline]
pub fn run_forever<F>(&self, callback: F) pub fn run_forever<F>(&mut self, callback: F)
where F: FnMut(Event) where F: FnMut(Event) -> ControlFlow
{ {
self.events_loop.run_forever(callback) self.events_loop.run_forever(callback)
} }
/// If we called `run_forever()`, stops the process of waiting for events. /// Creates an `EventsLoopProxy` that can be used to wake up the `EventsLoop` from another
// TODO: what if we're waiting from multiple threads? /// thread.
#[inline] pub fn create_proxy(&self) -> EventsLoopProxy {
pub fn interrupt(&self) { EventsLoopProxy {
self.events_loop.interrupt() 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`"
} }
} }

View file

@ -3,9 +3,7 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::Arc; use std::sync::Arc;
use CreationError; use {CreationError, CursorState, EventsLoopClosed, MouseCursor, ControlFlow};
use CursorState;
use MouseCursor;
use libc; use libc;
use self::x11::XConnection; use self::x11::XConnection;
@ -131,9 +129,10 @@ impl MonitorId {
impl Window2 { impl Window2 {
#[inline] #[inline]
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>, window: &::WindowAttributes, pub fn new(events_loop: &EventsLoop,
window: &::WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes) pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window2, CreationError> -> Result<Self, CreationError>
{ {
match *UNIX_BACKEND { match *UNIX_BACKEND {
UnixBackend::Wayland(ref ctxt) => { UnixBackend::Wayland(ref ctxt) => {
@ -309,6 +308,11 @@ pub enum EventsLoop {
X(x11::EventsLoop) X(x11::EventsLoop)
} }
pub enum EventsLoopProxy {
X(x11::EventsLoopProxy),
Wayland(wayland::EventsLoopProxy),
}
impl EventsLoop { impl EventsLoop {
pub fn new() -> EventsLoop { pub fn new() -> EventsLoop {
match *UNIX_BACKEND { match *UNIX_BACKEND {
@ -326,28 +330,37 @@ impl EventsLoop {
} }
} }
pub fn interrupt(&self) { pub fn create_proxy(&self) -> EventsLoopProxy {
match *self { match *self {
EventsLoop::Wayland(ref evlp) => evlp.interrupt(), EventsLoop::Wayland(ref evlp) => EventsLoopProxy::Wayland(evlp.create_proxy()),
EventsLoop::X(ref evlp) => evlp.interrupt() EventsLoop::X(ref evlp) => EventsLoopProxy::X(evlp.create_proxy()),
} }
} }
pub fn poll_events<F>(&self, callback: F) pub fn poll_events<F>(&mut self, callback: F)
where F: FnMut(::Event) where F: FnMut(::Event)
{ {
match *self { match *self {
EventsLoop::Wayland(ref evlp) => evlp.poll_events(callback), EventsLoop::Wayland(ref mut evlp) => evlp.poll_events(callback),
EventsLoop::X(ref evlp) => evlp.poll_events(callback) EventsLoop::X(ref mut evlp) => evlp.poll_events(callback)
} }
} }
pub fn run_forever<F>(&self, callback: F) pub fn run_forever<F>(&mut self, callback: F)
where F: FnMut(::Event) where F: FnMut(::Event) -> ControlFlow
{ {
match *self { match *self {
EventsLoop::Wayland(ref evlp) => evlp.run_forever(callback), EventsLoop::Wayland(ref mut evlp) => evlp.run_forever(callback),
EventsLoop::X(ref 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(),
} }
} }
} }

View file

@ -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::{Arc, Mutex, Weak};
use std::sync::atomic::AtomicBool; use std::sync::atomic::{self, AtomicBool};
use super::{DecoratedHandler, WindowId, DeviceId, WaylandContext}; use super::{DecoratedHandler, WindowId, DeviceId, WaylandContext};
@ -53,7 +54,9 @@ impl EventsLoopSink {
::std::mem::replace(&mut self.callback, cb) ::std::mem::replace(&mut self.callback, cb)
} }
fn with_callback<F: FnOnce(&mut FnMut(::Event))>(&mut self, f: F) { fn with_callback<F>(&mut self, f: F)
where F: FnOnce(&mut FnMut(::Event)),
{
f(&mut *self.callback) f(&mut *self.callback)
} }
} }
@ -67,11 +70,40 @@ pub struct EventsLoop {
decorated_ids: Mutex<Vec<(usize, Arc<wl_surface::WlSurface>)>>, decorated_ids: Mutex<Vec<(usize, Arc<wl_surface::WlSurface>)>>,
// our sink, receiver of callbacks, shared with some handlers // our sink, receiver of callbacks, shared with some handlers
sink: Arc<Mutex<EventsLoopSink>>, sink: Arc<Mutex<EventsLoopSink>>,
// trigger interruption of the run
interrupted: AtomicBool,
// trigger cleanup of the dead surfaces // trigger cleanup of the dead surfaces
cleanup_needed: Arc<AtomicBool>, cleanup_needed: Arc<AtomicBool>,
hid: usize // Whether or not there is a pending `Awakened` event to be emitted.
pending_wakeup: Arc<AtomicBool>,
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<WaylandContext>,
pending_wakeup: Weak<AtomicBool>,
}
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 { impl EventsLoop {
@ -84,12 +116,19 @@ impl EventsLoop {
evq: Arc::new(Mutex::new(evq)), evq: Arc::new(Mutex::new(evq)),
decorated_ids: Mutex::new(Vec::new()), decorated_ids: Mutex::new(Vec::new()),
sink: sink, sink: sink,
interrupted: AtomicBool::new(false), pending_wakeup: Arc::new(AtomicBool::new(false)),
cleanup_needed: Arc::new(AtomicBool::new(false)), cleanup_needed: Arc::new(AtomicBool::new(false)),
hid: hid 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 // some internals that Window needs access to
pub fn get_window_init(&self) -> (Arc<Mutex<EventQueue>>, Arc<AtomicBool>) { pub fn get_window_init(&self) -> (Arc<Mutex<EventQueue>>, Arc<AtomicBool>) {
(self.evq.clone(), self.cleanup_needed.clone()) (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) { fn prune_dead_windows(&self) {
self.decorated_ids.lock().unwrap().retain(|&(_, ref w)| w.is_alive()); self.decorated_ids.lock().unwrap().retain(|&(_, ref w)| w.is_alive());
let mut evq_guard = self.evq.lock().unwrap(); let mut evq_guard = self.evq.lock().unwrap();
@ -136,7 +171,7 @@ impl EventsLoop {
} }
} }
pub fn poll_events<F>(&self, callback: F) pub fn poll_events<F>(&mut self, callback: F)
where F: FnMut(::Event) where F: FnMut(::Event)
{ {
// send pending requests to the server... // send pending requests to the server...
@ -160,6 +195,8 @@ impl EventsLoop {
self.ctxt.dispatch_pending(); self.ctxt.dispatch_pending();
evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost"); evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
self.emit_pending_wakeup();
{ {
let mut sink_guard = self.sink.lock().unwrap(); let mut sink_guard = self.sink.lock().unwrap();
@ -173,45 +210,63 @@ impl EventsLoop {
unsafe { sink_guard.set_callback(old_cb) }; 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() self.prune_dead_windows()
} }
} }
pub fn run_forever<F>(&self, callback: F) pub fn run_forever<F>(&mut self, mut callback: F)
where F: FnMut(::Event) where F: FnMut(::Event) -> ControlFlow,
{ {
self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed);
// send pending requests to the server... // send pending requests to the server...
self.ctxt.flush(); self.ctxt.flush();
// first of all, get exclusive access to this event queue // first of all, get exclusive access to this event queue
let mut evq_guard = self.evq.lock().unwrap(); 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 // set the callback into the sink
// we extend the lifetime of the closure to 'static to be able to put it in // 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 // 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<FnMut(_)>) }; let static_cb = unsafe { ::std::mem::transmute(Box::new(callback) as Box<FnMut(_)>) };
let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) }; 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(); self.ctxt.dispatch();
evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost"); evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
self.emit_pending_wakeup();
let ids_guard = self.decorated_ids.lock().unwrap(); let ids_guard = self.decorated_ids.lock().unwrap();
self.sink.lock().unwrap().with_callback( self.sink.lock().unwrap()
|cb| Self::process_resize(&mut evq_guard, &ids_guard, cb) .with_callback(|cb| Self::process_resize(&mut evq_guard, &ids_guard, cb));
);
self.ctxt.flush(); 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() self.prune_dead_windows()
} }
if let ControlFlow::Break = control_flow.get() {
break;
}
} }
// replace the old noop callback // replace the old noop callback
unsafe { self.sink.lock().unwrap().set_callback(old_cb) }; 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 { enum KbdType {

View file

@ -1,7 +1,7 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
pub use self::window::{Window, WindowId}; 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, pub use self::context::{WaylandContext, MonitorId, get_available_monitors,
get_primary_monitor}; get_primary_monitor};

View file

@ -7,10 +7,12 @@ pub use self::xdisplay::{XConnection, XNotSupported, XError};
pub mod ffi; pub mod ffi;
use platform::PlatformSpecificWindowBuilderAttributes; 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::{mem, ptr, slice};
use std::sync::{Arc, Mutex, Weak}; use std::sync::{Arc, Mutex, Weak};
use std::sync::atomic::{self, AtomicBool};
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::CStr; use std::ffi::CStr;
@ -29,13 +31,22 @@ mod xdisplay;
// the one generated by the macro. // the one generated by the macro.
pub struct EventsLoop { pub struct EventsLoop {
interrupted: ::std::sync::atomic::AtomicBool,
display: Arc<XConnection>, display: Arc<XConnection>,
wm_delete_window: ffi::Atom, wm_delete_window: ffi::Atom,
windows: Mutex<HashMap<WindowId, WindowData>>, windows: Arc<Mutex<HashMap<WindowId, WindowData>>>,
devices: Mutex<HashMap<DeviceId, Device>>, devices: Mutex<HashMap<DeviceId, Device>>,
xi2ext: XExtension, xi2ext: XExtension,
pending_wakeup: Arc<AtomicBool>,
root: ffi::Window, 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<AtomicBool>,
display: Weak<XConnection>,
wakeup_dummy_window: ffi::Window,
} }
impl EventsLoop { impl EventsLoop {
@ -72,14 +83,22 @@ impl EventsLoop {
let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) }; 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 { let result = EventsLoop {
interrupted: ::std::sync::atomic::AtomicBool::new(false), pending_wakeup: Arc::new(AtomicBool::new(false)),
display: display, display: display,
wm_delete_window: wm_delete_window, wm_delete_window: wm_delete_window,
windows: Mutex::new(HashMap::new()), windows: Arc::new(Mutex::new(HashMap::new())),
devices: Mutex::new(HashMap::new()), devices: Mutex::new(HashMap::new()),
xi2ext: xi2ext, xi2ext: xi2ext,
root: root, root: root,
wakeup_dummy_window: wakeup_dummy_window,
}; };
{ {
@ -101,32 +120,17 @@ impl EventsLoop {
result result
} }
pub fn interrupt(&self) { pub fn create_proxy(&self) -> EventsLoopProxy {
self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed); EventsLoopProxy {
pending_wakeup: Arc::downgrade(&self.pending_wakeup),
// Push an event on the X event queue so that methods like run_forever will advance. display: Arc::downgrade(&self.display),
let mut xev = ffi::XClientMessageEvent { wakeup_dummy_window: self.wakeup_dummy_window,
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 poll_events<F>(&self, mut callback: F) pub fn poll_events<F>(&mut self, mut callback: F)
where F: FnMut(Event) where F: FnMut(Event)
{ {
self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed);
let xlib = &self.display.xlib; let xlib = &self.display.xlib;
let mut xev = unsafe { mem::uninitialized() }; let mut xev = unsafe { mem::uninitialized() };
@ -142,16 +146,13 @@ impl EventsLoop {
(xlib.XNextEvent)(self.display.display, &mut xev); (xlib.XNextEvent)(self.display.display, &mut xev);
} }
self.process_event(&mut xev, &mut callback); self.process_event(&mut xev, &mut callback);
if self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) {
break;
}
} }
} }
pub fn run_forever<F>(&self, mut callback: F) pub fn run_forever<F>(&mut self, mut callback: F)
where F: FnMut(Event) 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; let xlib = &self.display.xlib;
@ -159,8 +160,21 @@ impl EventsLoop {
loop { loop {
unsafe { (xlib.XNextEvent)(self.display.display, &mut xev) }; // Blocks as necessary 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; break;
} }
} }
@ -172,7 +186,7 @@ impl EventsLoop {
device.name.clone() device.name.clone()
} }
fn process_event<F>(&self, xev: &mut ffi::XEvent, callback: &mut F) fn process_event<F>(&self, xev: &mut ffi::XEvent, mut callback: F)
where F: FnMut(Event) where F: FnMut(Event)
{ {
let xlib = &self.display.xlib; 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 { if client_msg.data.get_long(0) as ffi::Atom == self.wm_delete_window {
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Closed }) callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Closed })
} else { } else {
// FIXME: Prone to spurious wakeups if self.pending_wakeup.load(atomic::Ordering::Relaxed) {
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Awakened }) self.pending_wakeup.store(false, atomic::Ordering::Relaxed);
callback(Event::Awakened);
}
} }
} }
@ -306,7 +322,11 @@ impl EventsLoop {
}; };
for chr in written.chars() { 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> { struct DeviceInfo<'a> {
display: &'a XConnection, display: &'a XConnection,
info: *const ffi::XIDeviceInfo, info: *const ffi::XIDeviceInfo,
@ -564,7 +624,8 @@ pub struct DeviceId(c_int);
pub struct Window2 { pub struct Window2 {
pub window: Arc<Window>, pub window: Arc<Window>,
events_loop: Weak<::platform::EventsLoop>, display: Weak<XConnection>,
windows: Weak<Mutex<HashMap<WindowId, WindowData>>>,
} }
impl ::std::ops::Deref for Window2 { 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 { impl Window2 {
pub fn new(events_loop: Arc<::platform::EventsLoop>, pub fn new(events_loop: &::platform::EventsLoop,
window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) window: &::WindowAttributes,
-> Result<Window2, CreationError> pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Self, CreationError>
{ {
let x_events_loop = if let ::platform::EventsLoop::X(ref e) = *events_loop { e } else { unreachable!() }; 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))); let win = ::std::sync::Arc::new(try!(Window::new(&x_events_loop, window, pl_attribs)));
@ -623,7 +685,8 @@ impl Window2 {
Ok(Window2 { Ok(Window2 {
window: win, 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 { impl Drop for Window2 {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(ev) = self.events_loop.upgrade() { if let (Some(windows), Some(display)) = (self.windows.upgrade(), self.display.upgrade()) {
if let ::platform::EventsLoop::X(ref ev) = *ev { let mut windows = windows.lock().unwrap();
let mut windows = ev.windows.lock().unwrap(); let w = windows.remove(&self.window.id()).unwrap();
let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap();
unsafe {
let w = windows.remove(&self.window.id()).unwrap(); (display.xlib.XDestroyIC)(w.ic);
let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap(); (display.xlib.XCloseIM)(w.im);
unsafe {
(ev.display.xlib.XDestroyIC)(w.ic);
(ev.display.xlib.XCloseIM)(w.im);
}
} }
} }
} }

View file

@ -166,8 +166,7 @@ impl Window {
}; };
// getting the root window // getting the root window
let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) }; let root = ctx.root;
display.check_errors().expect("Failed to get root window");
// creating // creating
let mut set_win_attr = { let mut set_win_attr = {

View file

@ -1,17 +1,23 @@
use {ControlFlow, EventsLoopClosed};
use cocoa::{self, appkit, foundation}; use cocoa::{self, appkit, foundation};
use cocoa::appkit::{NSApplication, NSEvent, NSView, NSWindow}; use cocoa::appkit::{NSApplication, NSEvent, NSView, NSWindow};
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, ModifiersState, KeyboardInput}; 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 super::window::Window;
use std; use std;
use super::DeviceId; use super::DeviceId;
pub struct EventsLoop { pub struct EventsLoop {
pub windows: std::sync::Mutex<Vec<std::sync::Weak<Window>>>, modifiers: Modifiers,
pub pending_events: std::sync::Mutex<std::collections::VecDeque<Event>>, pub shared: Arc<Shared>,
modifiers: std::sync::Mutex<Modifiers>, }
interrupted: std::sync::atomic::AtomicBool,
// State shared between the `EventsLoop` and its registered windows.
pub struct Shared {
pub windows: Mutex<Vec<Weak<Window>>>,
pub pending_events: Mutex<VecDeque<Event>>,
// The user event callback given via either of the `poll_events` or `run_forever` methods. // 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 // 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, user_callback: UserCallback,
} }
pub struct Proxy {}
struct Modifiers { struct Modifiers {
shift_pressed: bool, shift_pressed: bool,
ctrl_pressed: bool, ctrl_pressed: bool,
@ -34,14 +42,76 @@ struct Modifiers {
// //
// - ensure the callback pointer is never accidentally cloned // - ensure the callback pointer is never accidentally cloned
// - ensure that only the `EventsLoop` can `store` and `drop` the callback pointer // - 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 { pub struct UserCallback {
mutex: std::sync::Mutex<Option<*mut FnMut(Event)>>, mutex: Mutex<Option<*mut FnMut(Event)>>,
} }
unsafe impl Send for UserCallback {} impl Shared {
unsafe impl Sync for UserCallback {}
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 { impl UserCallback {
@ -92,22 +162,13 @@ impl UserCallback {
impl EventsLoop { impl EventsLoop {
pub fn new() -> Self { pub fn new() -> Self {
let modifiers = Modifiers {
shift_pressed: false,
ctrl_pressed: false,
win_pressed: false,
alt_pressed: false,
};
EventsLoop { EventsLoop {
windows: std::sync::Mutex::new(Vec::new()), shared: Arc::new(Shared::new()),
pending_events: std::sync::Mutex::new(std::collections::VecDeque::new()), modifiers: Modifiers::new(),
modifiers: std::sync::Mutex::new(modifiers),
interrupted: std::sync::atomic::AtomicBool::new(false),
user_callback: UserCallback { mutex: std::sync::Mutex::new(None) },
} }
} }
pub fn poll_events<F>(&self, mut callback: F) pub fn poll_events<F>(&mut self, mut callback: F)
where F: FnMut(Event), where F: FnMut(Event),
{ {
unsafe { 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 as long as we have pending events to return.
loop { loop {
unsafe { unsafe {
// First, yield all pending events. // 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); let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
@ -139,32 +200,42 @@ impl EventsLoop {
match event { match event {
// Call the user's callback. // 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, None => break,
} }
} }
} }
self.user_callback.drop(); self.shared.user_callback.drop();
} }
pub fn run_forever<F>(&self, mut callback: F) pub fn run_forever<F>(&mut self, mut callback: F)
where F: FnMut(Event) where F: FnMut(Event) -> ControlFlow
{ {
self.interrupted.store(false, std::sync::atomic::Ordering::Relaxed);
unsafe { unsafe {
if !msg_send![cocoa::base::class("NSThread"), isMainThread] { if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
panic!("Events can only be polled from the main thread on macOS"); 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 { loop {
unsafe { unsafe {
// First, yield all pending events. // 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); let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
@ -182,84 +253,19 @@ impl EventsLoop {
let _: () = msg_send![pool, release]; let _: () = msg_send![pool, release];
if let Some(event) = maybe_event { 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(); self.shared.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);
}
} }
// Convert some given `NSEvent` into a winit `Event`. // Convert some given `NSEvent` into a winit `Event`.
unsafe fn ns_event_to_event(&self, ns_event: cocoa::base::id) -> Option<Event> { unsafe fn ns_event_to_event(&mut self, ns_event: cocoa::base::id) -> Option<Event> {
if ns_event == cocoa::base::nil { if ns_event == cocoa::base::nil {
return None; return None;
} }
@ -285,9 +291,9 @@ impl EventsLoop {
_ => appkit::NSApp().sendEvent_(ns_event), _ => appkit::NSApp().sendEvent_(ns_event),
} }
let windows = self.windows.lock().unwrap(); let windows = self.shared.windows.lock().unwrap();
let maybe_window = windows.iter() let maybe_window = windows.iter()
.filter_map(std::sync::Weak::upgrade) .filter_map(Weak::upgrade)
.find(|window| window_id == window.id()); .find(|window| window_id == window.id());
let into_event = |window_event| Event::WindowEvent { 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. // Returns `Some` window if one of our windows is the key window.
let maybe_key_window = || windows.iter() let maybe_key_window = || windows.iter()
.filter_map(std::sync::Weak::upgrade) .filter_map(Weak::upgrade)
.find(|window| { .find(|window| {
let is_key_window: cocoa::base::BOOL = msg_send![*window.window, isKeyWindow]; let is_key_window: cocoa::base::BOOL = msg_send![*window.window, isKeyWindow];
is_key_window == cocoa::base::YES is_key_window == cocoa::base::YES
@ -326,7 +332,7 @@ impl EventsLoop {
let window_event = WindowEvent::ReceivedCharacter(received_char); let window_event = WindowEvent::ReceivedCharacter(received_char);
events.push_back(into_event(window_event)); 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)) Some(into_event(window_event))
}, },
@ -348,8 +354,6 @@ impl EventsLoop {
}, },
appkit::NSFlagsChanged => { appkit::NSFlagsChanged => {
let mut modifiers = self.modifiers.lock().unwrap();
unsafe fn modifier_event(event: cocoa::base::id, unsafe fn modifier_event(event: cocoa::base::id,
keymask: appkit::NSEventModifierFlags, keymask: appkit::NSEventModifierFlags,
key: events::VirtualKeyCode, key: events::VirtualKeyCode,
@ -392,41 +396,41 @@ impl EventsLoop {
if let Some(window_event) = modifier_event(ns_event, if let Some(window_event) = modifier_event(ns_event,
appkit::NSShiftKeyMask, appkit::NSShiftKeyMask,
events::VirtualKeyCode::LShift, 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)); events.push_back(into_event(window_event));
} }
if let Some(window_event) = modifier_event(ns_event, if let Some(window_event) = modifier_event(ns_event,
appkit::NSControlKeyMask, appkit::NSControlKeyMask,
events::VirtualKeyCode::LControl, 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)); events.push_back(into_event(window_event));
} }
if let Some(window_event) = modifier_event(ns_event, if let Some(window_event) = modifier_event(ns_event,
appkit::NSCommandKeyMask, appkit::NSCommandKeyMask,
events::VirtualKeyCode::LWin, 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)); events.push_back(into_event(window_event));
} }
if let Some(window_event) = modifier_event(ns_event, if let Some(window_event) = modifier_event(ns_event,
appkit::NSAlternateKeyMask, appkit::NSAlternateKeyMask,
events::VirtualKeyCode::LAlt, 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)); events.push_back(into_event(window_event));
} }
let event = events.pop_front(); 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 event
}, },
@ -505,7 +509,7 @@ impl EventsLoop {
appkit::NSApplicationDefined => match ns_event.subtype() { appkit::NSApplicationDefined => match ns_event.subtype() {
appkit::NSEventSubtype::NSApplicationActivatedEventType => { appkit::NSEventSubtype::NSApplicationActivatedEventType => {
Some(into_event(WindowEvent::Awakened)) Some(Event::Awakened)
}, },
_ => None, _ => 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(())
}
} }

View file

@ -1,8 +1,9 @@
#![cfg(target_os = "macos")] #![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::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window}; pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window};
use std::sync::Arc;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId; pub struct DeviceId;
@ -10,7 +11,7 @@ pub struct DeviceId;
use {CreationError}; use {CreationError};
pub struct Window2 { pub struct Window2 {
pub window: ::std::sync::Arc<Window>, pub window: Arc<Window>,
} }
impl ::std::ops::Deref for Window2 { impl ::std::ops::Deref for Window2 {
@ -23,14 +24,14 @@ impl ::std::ops::Deref for Window2 {
impl Window2 { impl Window2 {
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>, pub fn new(events_loop: &EventsLoop,
attributes: &::WindowAttributes, attributes: &::WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError> pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError>
{ {
let weak_events_loop = ::std::sync::Arc::downgrade(&events_loop); let weak_shared = Arc::downgrade(&events_loop.shared);
let window = ::std::sync::Arc::new(try!(Window::new(weak_events_loop, attributes, pl_attribs))); let window = Arc::new(try!(Window::new(weak_shared, attributes, pl_attribs)));
let weak_window = ::std::sync::Arc::downgrade(&window); let weak_window = Arc::downgrade(&window);
events_loop.windows.lock().unwrap().push(weak_window); events_loop.shared.windows.lock().unwrap().push(weak_window);
Ok(Window2 { window: window }) Ok(Window2 { window: window })
} }

View file

@ -5,6 +5,7 @@ use libc;
use WindowAttributes; use WindowAttributes;
use native_monitor::NativeMonitorId; use native_monitor::NativeMonitorId;
use os::macos::ActivationPolicy; use os::macos::ActivationPolicy;
use os::macos::WindowExt;
use objc; use objc;
use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; use objc::runtime::{Class, Object, Sel, BOOL, YES, NO};
@ -20,8 +21,9 @@ use core_graphics::display::{CGAssociateMouseAndMouseCursorPosition, CGMainDispl
use std; use std;
use std::ops::Deref; use std::ops::Deref;
use std::os::raw::c_void; 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)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -30,7 +32,7 @@ pub struct Id(pub usize);
struct DelegateState { struct DelegateState {
view: IdRef, view: IdRef,
window: IdRef, window: IdRef,
events_loop: std::sync::Weak<super::EventsLoop>, shared: Weak<Shared>,
} }
pub struct WindowDelegate { pub struct WindowDelegate {
@ -51,8 +53,8 @@ impl WindowDelegate {
event: window_event, event: window_event,
}; };
if let Some(events_loop) = state.events_loop.upgrade() { if let Some(shared) = state.shared.upgrade() {
events_loop.call_user_callback_with_event_or_store_in_pending(event); shared.call_user_callback_with_event_or_store_in_pending(event);
} }
} }
@ -71,10 +73,10 @@ impl WindowDelegate {
let state = &mut *(state as *mut DelegateState); let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::Closed); emit_event(state, WindowEvent::Closed);
// Remove the window from the events_loop. // Remove the window from the shared state.
if let Some(events_loop) = state.events_loop.upgrade() { if let Some(shared) = state.shared.upgrade() {
let window_id = get_window_id(*state.window); let window_id = get_window_id(*state.window);
events_loop.find_and_remove_window(window_id); shared.find_and_remove_window(window_id);
} }
} }
YES YES
@ -188,8 +190,8 @@ impl Drop for Window {
fn drop(&mut self) { fn drop(&mut self) {
// Remove this window from the `EventLoop`s list of windows. // Remove this window from the `EventLoop`s list of windows.
let id = self.id(); let id = self.id();
if let Some(ev) = self.delegate.state.events_loop.upgrade() { if let Some(shared) = self.delegate.state.shared.upgrade() {
ev.find_and_remove_window(id); shared.find_and_remove_window(id);
} }
// Close the window if it has not yet been closed. // Close the window if it has not yet been closed.
@ -215,7 +217,7 @@ impl WindowExt for Window {
} }
impl Window { impl Window {
pub fn new(events_loop: std::sync::Weak<super::EventsLoop>, pub fn new(shared: Weak<Shared>,
win_attribs: &WindowAttributes, win_attribs: &WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes) pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError> -> Result<Window, CreationError>
@ -266,7 +268,7 @@ impl Window {
let ds = DelegateState { let ds = DelegateState {
view: view.clone(), view: view.clone(),
window: window.clone(), window: window.clone(),
events_loop: events_loop, shared: shared,
}; };
let window = Window { let window = Window {

View file

@ -410,8 +410,9 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
}, },
x if x == *super::WAKEUP_MSG_ID => { x if x == *super::WAKEUP_MSG_ID => {
use events::WindowEvent::Awakened; // TODO: `Awakened` has been moved from the `WindowEvent` enum to the `Event` enum.
send_event(window, Awakened); // This code needs to be updated to reflect this change.
//send_event(window, ::Event::Awakened);
0 0
}, },

View file

@ -110,7 +110,7 @@ impl WindowBuilder {
} }
// building // 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 }) Ok(Window { window: w })
} }

View file

@ -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<T: Send + Sync>() {}
check_send_sync::<winit::EventsLoop>();
}

View file

@ -0,0 +1,8 @@
extern crate winit;
#[test]
fn events_loop_proxy_send() {
// ensures that `winit::EventsLoopProxy` implements `Send`
fn needs_send<T:Send>() {}
needs_send::<winit::EventsLoopProxy>();
}

View file

@ -1,9 +0,0 @@
extern crate winit;
#[cfg(feature = "window")]
#[test]
fn window_proxy_send() {
// ensures that `winit::WindowProxy` implements `Send`
fn needs_send<T:Send>() {}
needs_send::<winit::WindowProxy>();
}