From 06a5ec35b363effc9ccdfeb451b8a724d8716561 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 25 May 2017 02:29:51 +1000 Subject: [PATCH 01/20] [WIP] Remove Sync and Clone from EventsLoop. Add EventsLoopProxy. This commit only updates the top-level API to get some early feedback. None of the platform-specific code has been updated yet. I'm hoping to get around to this over the next couple days however if someone more familiar with the windows backend would like to do a PR against this fork that would be a great help. Closes #187. --- examples/proxy.rs | 29 +++++++++++++++++++++++++++++ src/lib.rs | 29 +++++++++++++++++++++++++---- tests/events_loop.rs | 10 ---------- 3 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 examples/proxy.rs delete mode 100644 tests/events_loop.rs diff --git a/examples/proxy.rs b/examples/proxy.rs new file mode 100644 index 000000000..7f82788be --- /dev/null +++ b/examples/proxy.rs @@ -0,0 +1,29 @@ +extern crate winit; + +fn main() { + let 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(); + } + }); + + events_loop.run_forever(|event| { + println!("{:?}", event); + match event { + winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => + events_loop.interrupt(), + _ => () + } + }); +} diff --git a/src/lib.rs b/src/lib.rs index 623f03c3f..abaaddb99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,10 +186,15 @@ 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, +} + +/// Used to wake up the `EventsLoop` from another thread. +pub struct EventsLoopProxy { + events_loop_proxy: platform::EventsLoopProxy, } impl EventsLoop { @@ -218,11 +223,27 @@ impl EventsLoop { } /// 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: platform::EventsLoopProxy::new(&self.events_loop), + } + } +} + +impl EventsLoopProxy { + /// Wake up the `EventsLoop` from which this proxy was created. + /// + /// This causes the `EventsLoop` to emit an `Awakened` event. + pub fn wakeup(&self) { + self.events_loop_proxy.wakeup(); + } } /// Object that allows you to build windows. diff --git a/tests/events_loop.rs b/tests/events_loop.rs deleted file mode 100644 index 85b3bb6b0..000000000 --- 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::(); -} From c8e791b402391020e21a64b4be29943850bee293 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 25 May 2017 23:18:56 +1000 Subject: [PATCH 02/20] Add a test that checks that EventsLoopProxy impls Send --- tests/events_loop_proxy_send.rs | 8 ++++++++ tests/window_proxy_send.rs | 9 --------- 2 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 tests/events_loop_proxy_send.rs delete mode 100644 tests/window_proxy_send.rs diff --git a/tests/events_loop_proxy_send.rs b/tests/events_loop_proxy_send.rs new file mode 100644 index 000000000..5c44aa13d --- /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 7ad626411..000000000 --- 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::(); -} From f6587aed39bdad2e4f4e4ce0b0281e8ecc3b20f1 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 25 May 2017 23:19:13 +1000 Subject: [PATCH 03/20] [WIP] Have EventsLoopProxy::wakeup return a Result. Begin linux impl. X11 and Wayland implementations are now half implemented, however both still do not correctly break from the inner blocking event dispatch functions when `wakeup` is called, which they should do. --- examples/proxy.rs | 2 +- src/events.rs | 3 +- src/lib.rs | 37 ++++++++++--- src/platform/linux/mod.rs | 25 ++++++++- src/platform/linux/wayland/event_loop.rs | 70 +++++++++++++++++++++--- src/platform/linux/wayland/mod.rs | 2 +- src/platform/linux/x11/mod.rs | 44 ++++++++++++--- 7 files changed, 149 insertions(+), 34 deletions(-) diff --git a/examples/proxy.rs b/examples/proxy.rs index 7f82788be..c3d615a2e 100644 --- a/examples/proxy.rs +++ b/examples/proxy.rs @@ -14,7 +14,7 @@ fn main() { // Wake up the `events_loop` once every second. loop { std::thread::sleep(std::time::Duration::from_secs(1)); - proxy.wakeup(); + proxy.wakeup().unwrap(); } }); diff --git a/src/events.rs b/src/events.rs index ac5e345c1..a508b589f 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 abaaddb99..d583b2678 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -189,12 +189,7 @@ pub struct ButtonId(u32); /// /// To wake up an `EventsLoop` from a another thread, see the `EventsLoopProxy` docs. pub struct EventsLoop { - events_loop: platform::EventsLoop, -} - -/// Used to wake up the `EventsLoop` from another thread. -pub struct EventsLoopProxy { - events_loop_proxy: platform::EventsLoopProxy, + events_loop: Arc, } impl EventsLoop { @@ -232,17 +227,41 @@ impl EventsLoop { /// thread. pub fn create_proxy(&self) -> EventsLoopProxy { EventsLoopProxy { - events_loop_proxy: platform::EventsLoopProxy::new(&self.events_loop), + 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. - pub fn wakeup(&self) { - self.events_loop_proxy.wakeup(); + /// + /// 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 18924b87b..fdc45d461 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}; use libc; use self::x11::XConnection; @@ -309,6 +307,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,6 +329,13 @@ impl EventsLoop { } } + pub fn create_proxy(&self) -> EventsLoopProxy { + match *self { + EventsLoop::Wayland(ref evlp) => EventsLoopProxy::Wayland(evlp.create_proxy()), + EventsLoop::X(ref evlp) => EventsLoopProxy::X(evlp.create_proxy()), + } + } + pub fn interrupt(&self) { match *self { EventsLoop::Wayland(ref evlp) => evlp.interrupt(), @@ -351,3 +361,12 @@ impl EventsLoop { } } } + +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 b86e46359..8f6f066b9 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -1,7 +1,7 @@ -use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState, KeyboardInput}; +use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState, KeyboardInput, EventsLoopClosed}; -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}; @@ -71,7 +71,38 @@ pub struct EventsLoop { 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); + // TODO: + // Cause the `EventsLoop` to break from `dispatch` if it is currently blocked. + ctxt.display.sync(); + Ok(()) + }, + _ => Err(EventsLoopClosed), + } + } } impl EventsLoop { @@ -85,11 +116,19 @@ impl EventsLoop { 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()) @@ -120,7 +159,7 @@ impl EventsLoop { } pub fn interrupt(&self) { - self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed); + self.interrupted.store(true, atomic::Ordering::Relaxed); } fn prune_dead_windows(&self) { @@ -160,6 +199,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,7 +214,7 @@ 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() } } @@ -181,7 +222,7 @@ impl EventsLoop { pub fn run_forever(&self, callback: F) where F: FnMut(::Event) { - self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed); + self.interrupted.store(false, atomic::Ordering::Relaxed); // send pending requests to the server... self.ctxt.flush(); @@ -195,16 +236,19 @@ impl EventsLoop { 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) { + while !self.interrupted.load(atomic::Ordering::Relaxed) { 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.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() } } @@ -212,6 +256,14 @@ impl EventsLoop { // 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 f267669b2..46665ee37 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 7535e42e0..c69ce0292 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -7,10 +7,11 @@ 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}; 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,15 +30,20 @@ mod xdisplay; // the one generated by the macro. pub struct EventsLoop { - interrupted: ::std::sync::atomic::AtomicBool, + interrupted: AtomicBool, display: Arc, wm_delete_window: ffi::Atom, windows: Mutex>, devices: Mutex>, xi2ext: XExtension, + pending_wakeup: Arc, root: ffi::Window, } +pub struct EventsLoopProxy { + pending_wakeup: Weak, +} + impl EventsLoop { pub fn new(display: Arc) -> EventsLoop { let wm_delete_window = unsafe { (display.xlib.XInternAtom)(display.display, b"WM_DELETE_WINDOW\0".as_ptr() as *const c_char, 0) }; @@ -73,7 +79,8 @@ impl EventsLoop { let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) }; let result = EventsLoop { - interrupted: ::std::sync::atomic::AtomicBool::new(false), + interrupted: AtomicBool::new(false), + pending_wakeup: Arc::new(AtomicBool::new(false)), display: display, wm_delete_window: wm_delete_window, windows: Mutex::new(HashMap::new()), @@ -101,8 +108,14 @@ impl EventsLoop { result } + pub fn create_proxy(&self) -> EventsLoopProxy { + EventsLoopProxy { + pending_wakeup: Arc::downgrade(&self.pending_wakeup), + } + } + pub fn interrupt(&self) { - self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed); + self.interrupted.store(true, atomic::Ordering::Relaxed); // Push an event on the X event queue so that methods like run_forever will advance. let mut xev = ffi::XClientMessageEvent { @@ -126,7 +139,7 @@ impl EventsLoop { pub fn poll_events(&self, mut callback: F) where F: FnMut(Event) { - self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed); + self.interrupted.store(false, atomic::Ordering::Relaxed); let xlib = &self.display.xlib; let mut xev = unsafe { mem::uninitialized() }; @@ -142,7 +155,7 @@ 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) { + if self.interrupted.load(atomic::Ordering::Relaxed) { break; } } @@ -151,7 +164,7 @@ impl EventsLoop { pub fn run_forever(&self, mut callback: F) where F: FnMut(Event) { - self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed); + self.interrupted.store(false, atomic::Ordering::Relaxed); let xlib = &self.display.xlib; @@ -160,7 +173,7 @@ 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) { + if self.interrupted.load(atomic::Ordering::Relaxed) { break; } } @@ -197,7 +210,7 @@ impl EventsLoop { callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Closed }) } else { // FIXME: Prone to spurious wakeups - callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Awakened }) + callback(Event::Awakened) } } @@ -523,6 +536,19 @@ impl EventsLoop { } } +impl EventsLoopProxy { + pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { + // Update the `EventsLoop`'s `pending_wakeup` flag. + match self.pending_wakeup.upgrade() { + Some(wakeup) => Ok(wakeup.store(true, atomic::Ordering::Relaxed)), + None => Err(EventsLoopClosed), + } + + // TODO: + // Cause the `EventsLoop` to break if it is currently blocked. + } +} + struct DeviceInfo<'a> { display: &'a XConnection, info: *const ffi::XIDeviceInfo, From 9ca2f8378401b14e06aee908900b317fd0951659 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sat, 27 May 2017 22:51:59 +1000 Subject: [PATCH 04/20] Call flush so that the wayland eventsloop correctly breaks from dispatch when wakeup is called --- src/platform/linux/wayland/event_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index 8f6f066b9..17b136a38 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -95,9 +95,9 @@ impl EventsLoopProxy { (Some(ctxt), Some(wakeup)) => { // Update the `EventsLoop`'s `pending_wakeup` flag. wakeup.store(true, atomic::Ordering::Relaxed); - // TODO: // Cause the `EventsLoop` to break from `dispatch` if it is currently blocked. ctxt.display.sync(); + ctxt.display.flush().ok(); Ok(()) }, _ => Err(EventsLoopClosed), From 339318f295c7884097caf57279809cbd78456772 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Wed, 31 May 2017 15:00:49 +1000 Subject: [PATCH 05/20] Update macOS backend to removal of Send+Sync and addition of EventsLoopProxy --- src/platform/macos/events_loop.rs | 55 ++++++++++++++++++------------- src/platform/macos/mod.rs | 2 +- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/platform/macos/events_loop.rs b/src/platform/macos/events_loop.rs index 5b075a69d..12fe2c0e6 100644 --- a/src/platform/macos/events_loop.rs +++ b/src/platform/macos/events_loop.rs @@ -1,3 +1,4 @@ +use EventsLoopClosed; use cocoa::{self, appkit, foundation}; use cocoa::appkit::{NSApplication, NSEvent, NSView, NSWindow}; use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, ModifiersState, KeyboardInput}; @@ -23,6 +24,8 @@ pub struct EventsLoop { user_callback: UserCallback, } +pub struct Proxy {} + struct Modifiers { shift_pressed: bool, ctrl_pressed: bool, @@ -40,9 +43,6 @@ pub struct UserCallback { } -unsafe impl Send for UserCallback {} -unsafe impl Sync for UserCallback {} - impl UserCallback { // Here we store user's `callback` behind the mutex so that they may be safely shared between @@ -197,25 +197,6 @@ impl EventsLoop { 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. @@ -505,7 +486,7 @@ impl EventsLoop { appkit::NSApplicationDefined => match ns_event.subtype() { appkit::NSEventSubtype::NSApplicationActivatedEventType => { - Some(into_event(WindowEvent::Awakened)) + Some(Event::Awakened) }, _ => None, }, @@ -514,6 +495,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 5b207bb2d..0e1451b68 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -1,6 +1,6 @@ #![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}; From 647a1727d0aacedcda8e46348cfc7527fb03e4c6 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Wed, 31 May 2017 15:35:08 +1000 Subject: [PATCH 06/20] Attempt to update api_transition to addition of EventsLoopProxy --- src/api_transition.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/api_transition.rs b/src/api_transition.rs index 56a259344..7cd14a1be 100644 --- a/src/api_transition.rs +++ b/src/api_transition.rs @@ -10,6 +10,11 @@ macro_rules! gen_api_transition { pub struct EventsLoop { windows: ::std::sync::Mutex>>, 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 { @@ -17,6 +22,7 @@ macro_rules! gen_api_transition { EventsLoop { windows: ::std::sync::Mutex::new(vec![]), interrupted: ::std::sync::atomic::AtomicBool::new(false), + awakened: ::std::sync::Arc::new(::std::sync::atomic::AtomicBool::new(false)), } } @@ -27,6 +33,11 @@ macro_rules! gen_api_transition { pub fn poll_events(&self, mut callback: F) where F: FnMut(::Event) { + if self.awakened.load(::std::sync::atomic::Ordering::Relaxed) { + self.awakened.store(false, ::std::sync::atomic::Ordering::Relaxed); + callback(::Event::Awakened); + } + let mut windows = self.windows.lock().unwrap(); for window in windows.iter() { for event in window.poll_events() { @@ -42,6 +53,7 @@ macro_rules! gen_api_transition { where F: FnMut(::Event) { 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 { @@ -52,6 +64,24 @@ macro_rules! gen_api_transition { } } } + + 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(()) + }, + } + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] From 8f0ef514b13d38398c5ecfd9ff0e4ab65ed43941 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Wed, 31 May 2017 15:52:15 +1000 Subject: [PATCH 07/20] Fix incorred Awakened import in windows backend --- src/platform/windows/callback.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platform/windows/callback.rs b/src/platform/windows/callback.rs index 864cf9c96..015ecec92 100644 --- a/src/platform/windows/callback.rs +++ b/src/platform/windows/callback.rs @@ -410,8 +410,7 @@ 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); + send_event(window, ::Event::Awakened); 0 }, From 2b55b2e0efb977a7e02fb5b3accc117c795cc688 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Wed, 31 May 2017 16:11:06 +1000 Subject: [PATCH 08/20] Temporarily remove windows window-specific awakened event. Needs to be updated to non-window-specific Event. --- src/platform/windows/callback.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/platform/windows/callback.rs b/src/platform/windows/callback.rs index 015ecec92..c11aac133 100644 --- a/src/platform/windows/callback.rs +++ b/src/platform/windows/callback.rs @@ -410,7 +410,9 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, }, x if x == *super::WAKEUP_MSG_ID => { - send_event(window, ::Event::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 }, From 38856b1c608bc9cbc89c01c2fdb5fba9c2823e44 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Wed, 31 May 2017 18:07:51 +1000 Subject: [PATCH 09/20] X11 - Move event insertion from interrupt to proxy wakeup. --- src/platform/linux/x11/mod.rs | 59 +++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index c69ce0292..eb98fb976 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -42,6 +42,8 @@ pub struct EventsLoop { pub struct EventsLoopProxy { pending_wakeup: Weak, + display: Weak, + root: ffi::Window, } impl EventsLoop { @@ -111,29 +113,13 @@ impl EventsLoop { pub fn create_proxy(&self) -> EventsLoopProxy { EventsLoopProxy { pending_wakeup: Arc::downgrade(&self.pending_wakeup), + display: Arc::downgrade(&self.display), + root: self.root, } } pub fn interrupt(&self) { self.interrupted.store(true, 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 poll_events(&self, mut callback: F) @@ -165,6 +151,7 @@ impl EventsLoop { where F: FnMut(Event) { self.interrupted.store(false, atomic::Ordering::Relaxed); + self.pending_wakeup.store(false, atomic::Ordering::Relaxed); let xlib = &self.display.xlib; @@ -172,6 +159,12 @@ impl EventsLoop { loop { unsafe { (xlib.XNextEvent)(self.display.display, &mut xev) }; // Blocks as necessary + + if self.pending_wakeup.load(atomic::Ordering::Relaxed) { + self.pending_wakeup.store(false, atomic::Ordering::Relaxed); + callback(Event::Awakened); + } + self.process_event(&mut xev, &mut callback); if self.interrupted.load(atomic::Ordering::Relaxed) { break; @@ -539,13 +532,33 @@ impl EventsLoop { impl EventsLoopProxy { pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { // Update the `EventsLoop`'s `pending_wakeup` flag. - match self.pending_wakeup.upgrade() { - Some(wakeup) => Ok(wakeup.store(true, atomic::Ordering::Relaxed)), - None => Err(EventsLoopClosed), + 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 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: display.display, + data: unsafe { mem::zeroed() }, + }; + + unsafe { + (display.xlib.XSendEvent)(display.display, self.root, 0, 0, mem::transmute(&mut xev)); + (display.xlib.XFlush)(display.display); + display.check_errors().expect("Failed to call XSendEvent after wakeup"); } - // TODO: - // Cause the `EventsLoop` to break if it is currently blocked. + Ok(()) } } From f2dd2f07524564ab42398a8fb6ee5d878548e1cb Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Fri, 2 Jun 2017 21:19:45 +1000 Subject: [PATCH 10/20] WIP - Make poll_events and run_forever take &mut self This removes the need for the EventsLoop::interrupt method by inroducing a ControlFlow type. This new type is to be returned by the user's callback and indicates whether the `EventsLoop` should continue waiting for events or break from the loop. Only the wayland, x11 and api_transition backends have been updated so far, and only the wayland backend has actually been tested. --- examples/window.rs | 6 +- src/api_transition.rs | 15 ++--- src/lib.rs | 29 +++++---- src/platform/linux/mod.rs | 28 ++++----- src/platform/linux/wayland/event_loop.rs | 39 ++++++------ src/platform/linux/x11/mod.rs | 79 +++++++++++++----------- src/window.rs | 2 +- 7 files changed, 103 insertions(+), 95 deletions(-) diff --git a/examples/window.rs b/examples/window.rs index c6d9327a2..921b7ea62 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -12,8 +12,10 @@ fn main() { println!("{:?}", event); match event { - winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(), - _ => () + winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => { + winit::ControlFlow::Complete + }, + _ => winit::ControlFlow::Continue, } }); } diff --git a/src/api_transition.rs b/src/api_transition.rs index 7cd14a1be..db2c617d9 100644 --- a/src/api_transition.rs +++ b/src/api_transition.rs @@ -8,7 +8,7 @@ macro_rules! gen_api_transition { () => { pub struct EventsLoop { - windows: ::std::sync::Mutex>>, + windows: ::std::sync::Arc<::std::sync::Mutex>>>, interrupted: ::std::sync::atomic::AtomicBool, awakened: ::std::sync::Arc<::std::sync::atomic::AtomicBool>, } @@ -20,7 +20,7 @@ macro_rules! gen_api_transition { impl EventsLoop { pub fn new() -> EventsLoop { EventsLoop { - windows: ::std::sync::Mutex::new(vec![]), + windows: ::std::sync::Arc::new(::std::sync::Mutex::new(vec![])), interrupted: ::std::sync::atomic::AtomicBool::new(false), awakened: ::std::sync::Arc::new(::std::sync::atomic::AtomicBool::new(false)), } @@ -92,7 +92,7 @@ macro_rules! gen_api_transition { pub struct Window2 { pub window: ::std::sync::Arc, - events_loop: ::std::sync::Weak, + windows: ::std::sync::Weak<::std::sync::Mutex>>> } impl ::std::ops::Deref for Window2 { @@ -104,7 +104,8 @@ macro_rules! gen_api_transition { } impl Window2 { - pub fn new(events_loop: ::std::sync::Arc, window: &::WindowAttributes, + pub fn new(events_loop: &EventsLoop, + window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result { @@ -112,7 +113,7 @@ macro_rules! gen_api_transition { events_loop.windows.lock().unwrap().push(win.clone()); Ok(Window2 { window: win, - events_loop: ::std::sync::Arc::downgrade(&events_loop), + events_loop: ::std::sync::Arc::downgrade(&events_loop.windows), }) } @@ -124,8 +125,8 @@ macro_rules! gen_api_transition { impl Drop for Window2 { fn drop(&mut self) { - if let Some(ev) = self.events_loop.upgrade() { - let mut windows = ev.windows.lock().unwrap(); + if let Some(windows) = self.windows.upgrade() { + let mut windows = windows.lock().unwrap(); windows.retain(|w| &**w as *const Window != &*self.window as *const _); } } diff --git a/src/lib.rs b/src/lib.rs index d583b2678..4097b83a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,8 +119,6 @@ extern crate x11_dl; #[macro_use(wayland_env,declare_handler)] extern crate wayland_client; -use std::sync::Arc; - pub use events::*; pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor}; pub use native_monitor::NativeMonitorId; @@ -189,21 +187,32 @@ pub struct ButtonId(u32); /// /// To wake up an `EventsLoop` from a another thread, see the `EventsLoopProxy` docs. pub struct EventsLoop { - events_loop: Arc, + events_loop: platform::EventsLoop, +} + +/// Returned by the user callback given to the `EventsLoop::run_forever` method. +/// +/// Indicates whether the `run_forever` method should continue or complete. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ControlFlow { + /// Continue looping and waiting for events. + Continue, + /// Exit from the event loop. + Complete, } impl EventsLoop { /// Builds a new events loop. pub fn new() -> EventsLoop { EventsLoop { - events_loop: Arc::new(platform::EventsLoop::new()), + events_loop: platform::EventsLoop::new(), } } /// Fetches all the events that are pending, calls the callback function for each of them, /// and returns. #[inline] - pub fn poll_events(&self, callback: F) + pub fn poll_events(&mut self, callback: F) where F: FnMut(Event) { self.events_loop.poll_events(callback) @@ -211,18 +220,12 @@ impl EventsLoop { /// Runs forever until `interrupt()` is called. Whenever an event happens, calls the callback. #[inline] - pub fn run_forever(&self, callback: F) - where F: FnMut(Event) + pub fn run_forever(&mut self, callback: F) + where F: FnMut(Event) -> ControlFlow { self.events_loop.run_forever(callback) } - /// If we called `run_forever()`, stops the process of waiting for events. - #[inline] - pub fn interrupt(&self) { - self.events_loop.interrupt() - } - /// Creates an `EventsLoopProxy` that can be used to wake up the `EventsLoop` from another /// thread. pub fn create_proxy(&self) -> EventsLoopProxy { diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index fdc45d461..6bc4719da 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -3,7 +3,7 @@ use std::collections::VecDeque; use std::sync::Arc; -use {CreationError, CursorState, EventsLoopClosed, MouseCursor}; +use {CreationError, CursorState, EventsLoopClosed, MouseCursor, ControlFlow}; use libc; use self::x11::XConnection; @@ -129,9 +129,10 @@ impl MonitorId { impl Window2 { #[inline] - pub fn new(events_loop: ::std::sync::Arc, window: &::WindowAttributes, + pub fn new(events_loop: &EventsLoop, + window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) - -> Result + -> Result { match *UNIX_BACKEND { UnixBackend::Wayland(ref ctxt) => { @@ -336,28 +337,21 @@ impl EventsLoop { } } - pub fn interrupt(&self) { - match *self { - EventsLoop::Wayland(ref evlp) => evlp.interrupt(), - EventsLoop::X(ref evlp) => evlp.interrupt() - } - } - - pub fn poll_events(&self, callback: F) + pub fn poll_events(&mut self, callback: F) where F: FnMut(::Event) { match *self { - EventsLoop::Wayland(ref evlp) => evlp.poll_events(callback), - EventsLoop::X(ref evlp) => evlp.poll_events(callback) + EventsLoop::Wayland(ref mut evlp) => evlp.poll_events(callback), + EventsLoop::X(ref mut evlp) => evlp.poll_events(callback) } } - pub fn run_forever(&self, callback: F) - where F: FnMut(::Event) + pub fn run_forever(&mut self, callback: F) + where F: FnMut(::Event) -> ControlFlow { match *self { - EventsLoop::Wayland(ref evlp) => evlp.run_forever(callback), - EventsLoop::X(ref evlp) => evlp.run_forever(callback) + EventsLoop::Wayland(ref mut evlp) => evlp.run_forever(callback), + EventsLoop::X(ref mut evlp) => evlp.run_forever(callback) } } } diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index 17b136a38..f2b1325e1 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -1,4 +1,5 @@ -use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState, KeyboardInput, EventsLoopClosed}; +use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState, + KeyboardInput, EventsLoopClosed, ControlFlow}; use std::sync::{Arc, Mutex, Weak}; use std::sync::atomic::{self, AtomicBool}; @@ -53,7 +54,9 @@ impl EventsLoopSink { ::std::mem::replace(&mut self.callback, cb) } - fn with_callback(&mut self, f: F) { + fn with_callback(&mut self, f: F) + where F: FnOnce(&mut FnMut(::Event)), + { f(&mut *self.callback) } } @@ -67,8 +70,6 @@ pub struct EventsLoop { decorated_ids: Mutex)>>, // our sink, receiver of callbacks, shared with some handlers sink: Arc>, - // trigger interruption of the run - interrupted: AtomicBool, // trigger cleanup of the dead surfaces cleanup_needed: Arc, // Whether or not there is a pending `Awakened` event to be emitted. @@ -115,7 +116,6 @@ impl EventsLoop { evq: Arc::new(Mutex::new(evq)), decorated_ids: Mutex::new(Vec::new()), sink: sink, - interrupted: AtomicBool::new(false), pending_wakeup: Arc::new(AtomicBool::new(false)), cleanup_needed: Arc::new(AtomicBool::new(false)), hid: hid @@ -158,10 +158,6 @@ impl EventsLoop { } } - pub fn interrupt(&self) { - self.interrupted.store(true, atomic::Ordering::Relaxed); - } - fn prune_dead_windows(&self) { self.decorated_ids.lock().unwrap().retain(|&(_, ref w)| w.is_alive()); let mut evq_guard = self.evq.lock().unwrap(); @@ -175,7 +171,7 @@ impl EventsLoop { } } - pub fn poll_events(&self, callback: F) + pub fn poll_events(&mut self, callback: F) where F: FnMut(::Event) { // send pending requests to the server... @@ -219,38 +215,45 @@ impl EventsLoop { } } - pub fn run_forever(&self, callback: F) - where F: FnMut(::Event) + pub fn run_forever(&mut self, mut callback: F) + where F: FnMut(::Event) -> ControlFlow, { - self.interrupted.store(false, atomic::Ordering::Relaxed); - // send pending requests to the server... self.ctxt.flush(); // first of all, get exclusive access to this event queue let mut evq_guard = self.evq.lock().unwrap(); + // Check for control flow by wrapping the callback. + let control_flow = ::std::cell::Cell::new(ControlFlow::Continue); + let callback = |event| if let ControlFlow::Complete = callback(event) { + control_flow.set(ControlFlow::Complete); + }; + // set the callback into the sink // we extend the lifetime of the closure to 'static to be able to put it in // the sink, but we'll explicitly drop it at the end of this function, so it's fine let static_cb = unsafe { ::std::mem::transmute(Box::new(callback) as Box) }; let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) }; - while !self.interrupted.load(atomic::Ordering::Relaxed) { + loop { self.ctxt.dispatch(); evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost"); self.emit_pending_wakeup(); let ids_guard = self.decorated_ids.lock().unwrap(); - self.sink.lock().unwrap().with_callback( - |cb| Self::process_resize(&mut evq_guard, &ids_guard, cb) - ); + self.sink.lock().unwrap() + .with_callback(|cb| Self::process_resize(&mut evq_guard, &ids_guard, cb)); self.ctxt.flush(); if self.cleanup_needed.swap(false, atomic::Ordering::Relaxed) { self.prune_dead_windows() } + + if let ControlFlow::Complete = control_flow.get() { + break; + } } // replace the old noop callback diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index eb98fb976..f18b986cd 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -7,7 +7,8 @@ pub use self::xdisplay::{XConnection, XNotSupported, XError}; pub mod ffi; use platform::PlatformSpecificWindowBuilderAttributes; -use {CreationError, Event, EventsLoopClosed, WindowEvent, DeviceEvent, AxisId, ButtonId, KeyboardInput}; +use {CreationError, Event, EventsLoopClosed, WindowEvent, DeviceEvent, AxisId, ButtonId, + KeyboardInput, ControlFlow}; use std::{mem, ptr, slice}; use std::sync::{Arc, Mutex, Weak}; @@ -30,10 +31,9 @@ mod xdisplay; // the one generated by the macro. pub struct EventsLoop { - interrupted: AtomicBool, display: Arc, wm_delete_window: ffi::Atom, - windows: Mutex>, + windows: Arc>>, devices: Mutex>, xi2ext: XExtension, pending_wakeup: Arc, @@ -81,11 +81,10 @@ impl EventsLoop { let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) }; let result = EventsLoop { - interrupted: AtomicBool::new(false), pending_wakeup: Arc::new(AtomicBool::new(false)), display: display, wm_delete_window: wm_delete_window, - windows: Mutex::new(HashMap::new()), + windows: Arc::new(Mutex::new(HashMap::new())), devices: Mutex::new(HashMap::new()), xi2ext: xi2ext, root: root, @@ -118,14 +117,9 @@ impl EventsLoop { } } - pub fn interrupt(&self) { - self.interrupted.store(true, atomic::Ordering::Relaxed); - } - pub fn poll_events(&self, mut callback: F) where F: FnMut(Event) { - self.interrupted.store(false, atomic::Ordering::Relaxed); let xlib = &self.display.xlib; let mut xev = unsafe { mem::uninitialized() }; @@ -141,16 +135,12 @@ impl EventsLoop { (xlib.XNextEvent)(self.display.display, &mut xev); } self.process_event(&mut xev, &mut callback); - if self.interrupted.load(atomic::Ordering::Relaxed) { - break; - } } } - pub fn run_forever(&self, mut callback: F) - where F: FnMut(Event) + pub fn run_forever(&mut self, mut callback: F) + where F: FnMut(Event) -> ControlFlow { - self.interrupted.store(false, atomic::Ordering::Relaxed); self.pending_wakeup.store(false, atomic::Ordering::Relaxed); let xlib = &self.display.xlib; @@ -160,13 +150,25 @@ impl EventsLoop { loop { unsafe { (xlib.XNextEvent)(self.display.display, &mut xev) }; // Blocks as necessary + let mut control_flow = ControlFlow::Continue; + if self.pending_wakeup.load(atomic::Ordering::Relaxed) { self.pending_wakeup.store(false, atomic::Ordering::Relaxed); - callback(Event::Awakened); + control_flow = callback(Event::Awakened); } - self.process_event(&mut xev, &mut callback); - if self.interrupted.load(atomic::Ordering::Relaxed) { + // Track whether or not `Complete` was returned when processing the event. + { + let mut cb = |event| { + if let ControlFlow::Complete = callback(event) { + control_flow = ControlFlow::Complete; + } + }; + + self.process_event(&mut xev, &mut cb); + } + + if let ControlFlow::Complete = control_flow { break; } } @@ -178,7 +180,7 @@ impl EventsLoop { device.name.clone() } - fn process_event(&self, xev: &mut ffi::XEvent, callback: &mut F) + fn process_event(&self, xev: &mut ffi::XEvent, mut callback: F) where F: FnMut(Event) { let xlib = &self.display.xlib; @@ -312,7 +314,11 @@ impl EventsLoop { }; for chr in written.chars() { - callback(Event::WindowEvent { window_id: wid, event: WindowEvent::ReceivedCharacter(chr) }) + let event = Event::WindowEvent { + window_id: wid, + event: WindowEvent::ReceivedCharacter(chr), + }; + callback(event); } } } @@ -603,7 +609,8 @@ pub struct DeviceId(c_int); pub struct Window2 { pub window: Arc, - events_loop: Weak<::platform::EventsLoop>, + display: Weak, + windows: Weak>>, } impl ::std::ops::Deref for Window2 { @@ -620,9 +627,10 @@ lazy_static! { // TODO: use a static mutex when that's possible, and put me } impl Window2 { - pub fn new(events_loop: Arc<::platform::EventsLoop>, - window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) - -> Result + pub fn new(events_loop: &::platform::EventsLoop, + window: &::WindowAttributes, + pl_attribs: &PlatformSpecificWindowBuilderAttributes) + -> Result { let x_events_loop = if let ::platform::EventsLoop::X(ref e) = *events_loop { e } else { unreachable!() }; let win = ::std::sync::Arc::new(try!(Window::new(&x_events_loop, window, pl_attribs))); @@ -662,7 +670,8 @@ impl Window2 { Ok(Window2 { window: win, - events_loop: Arc::downgrade(&events_loop), + windows: Arc::downgrade(&x_events_loop.windows), + display: Arc::downgrade(&x_events_loop.display), }) } @@ -674,17 +683,13 @@ impl Window2 { impl Drop for Window2 { fn drop(&mut self) { - if let Some(ev) = self.events_loop.upgrade() { - if let ::platform::EventsLoop::X(ref ev) = *ev { - let mut windows = ev.windows.lock().unwrap(); - - - let w = windows.remove(&self.window.id()).unwrap(); - let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap(); - unsafe { - (ev.display.xlib.XDestroyIC)(w.ic); - (ev.display.xlib.XCloseIM)(w.im); - } + if let (Some(windows), Some(display)) = (self.windows.upgrade(), self.display.upgrade()) { + let mut windows = windows.lock().unwrap(); + let w = windows.remove(&self.window.id()).unwrap(); + let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap(); + unsafe { + (display.xlib.XDestroyIC)(w.ic); + (display.xlib.XCloseIM)(w.im); } } } diff --git a/src/window.rs b/src/window.rs index 349c31b1a..94c677b07 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 }) } From db9e80bdb6eeacd30c5944ce9b6aa6571a1fb446 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 8 Jun 2017 00:12:41 +1000 Subject: [PATCH 11/20] Update examples and tests to addition of ControlFlow --- examples/cursor.rs | 7 ++++--- examples/fullscreen.rs | 13 ++++++++----- examples/grabbing.rs | 8 +++++--- examples/min_max_size.rs | 6 +++--- examples/multiwindow.rs | 5 +++-- examples/proxy.rs | 8 ++++---- examples/transparent.rs | 6 +++--- examples/window.rs | 4 ++-- src/lib.rs | 27 +++++++++++---------------- 9 files changed, 43 insertions(+), 41 deletions(-) diff --git a/examples/cursor.rs b/examples/cursor.rs index dcb379714..cd979b994 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::Complete; }, _ => () } + ControlFlow::Continue }); } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 1c1d8bdbc..921caa612 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::Complete, + WindowEvent::KeyboardInput { input: winit::KeyboardInput { virtual_keycode: Some(winit::VirtualKeyCode::Escape), .. }, .. - } => events_loop.interrupt(), + } => return ControlFlow::Complete, _ => () } }, _ => {} } + + ControlFlow::Continue }); } diff --git a/examples/grabbing.rs b/examples/grabbing.rs index 2ba327f43..fc6b7d7c3 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::Complete, 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 fe22f3e9d..54f03da70 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::Complete, + _ => winit::ControlFlow::Continue, } }); } diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 6c92b69ec..28f9955f2 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::Complete; } }, _ => (), } + winit::ControlFlow::Continue }) } diff --git a/examples/proxy.rs b/examples/proxy.rs index c3d615a2e..8741d7751 100644 --- a/examples/proxy.rs +++ b/examples/proxy.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(); @@ -22,8 +22,8 @@ fn main() { println!("{:?}", event); match event { winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => - events_loop.interrupt(), - _ => () + winit::ControlFlow::Complete, + _ => winit::ControlFlow::Continue, } }); } diff --git a/examples/transparent.rs b/examples/transparent.rs index 9e476dee3..da9a80e72 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::Complete, + _ => winit::ControlFlow::Continue, } }); } diff --git a/examples/window.rs b/examples/window.rs index 921b7ea62..fd5ef6e9b 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(); diff --git a/src/lib.rs b/src/lib.rs index 4097b83a0..74d3c5a87 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::Complete`. //! //! ```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::Complete //! }, -//! _ => () +//! _ => ControlFlow::Continue, //! } //! }); //! ``` @@ -137,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::Complete /// }, -/// _ => () +/// _ => ControlFlow::Continue, /// } /// }); /// ``` From c5b9bd361227e3df2fe58b80c87044e8507a7942 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Fri, 9 Jun 2017 19:40:22 +1000 Subject: [PATCH 12/20] Update macos backend to addition of ControlFlow (untested) --- src/platform/macos/events_loop.rs | 36 ++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/platform/macos/events_loop.rs b/src/platform/macos/events_loop.rs index 12fe2c0e6..bed3df567 100644 --- a/src/platform/macos/events_loop.rs +++ b/src/platform/macos/events_loop.rs @@ -1,4 +1,4 @@ -use EventsLoopClosed; +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}; @@ -11,7 +11,6 @@ pub struct EventsLoop { pub windows: std::sync::Mutex>>, pub pending_events: std::sync::Mutex>, modifiers: std::sync::Mutex, - interrupted: std::sync::atomic::AtomicBool, // The user event callback given via either of the `poll_events` or `run_forever` methods. // @@ -102,12 +101,11 @@ impl 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) }, } } - pub fn poll_events(&self, mut callback: F) + pub fn poll_events(&mut self, mut callback: F) where F: FnMut(Event), { unsafe { @@ -148,23 +146,33 @@ impl EventsLoop { self.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"); } } + // Track whether or not control flow has changed. + let mut control_flow = std::cell::Cell::new(ControlFlow::Continue); + + let mut callback = |event| { + if let ControlFlow::Complete = callback(event) { + control_flow.set(ControlFlow::Complete); + } + }; + self.user_callback.store(&mut callback); loop { unsafe { // First, yield all pending events. self.call_user_callback_with_pending_events(); + if let ControlFlow::Complete = control_flow.get() { + break; + } let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil); @@ -183,22 +191,16 @@ impl EventsLoop { if let Some(event) = maybe_event { self.user_callback.call_with_event(event); + if let ControlFlow::Complete = 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); - } - // Removes the window with the given `Id` from the `windows` list. // // This is called when a window is either `Closed` or `Drop`ped. From 02375269999f7ea10a85d5a683f9425e9ea3e3dd Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Fri, 9 Jun 2017 22:13:30 +1000 Subject: [PATCH 13/20] Complete macos backend update to addition of ControlFlow --- src/platform/macos/events_loop.rs | 181 +++++++++++++++++------------- src/platform/macos/mod.rs | 13 ++- src/platform/macos/window.rs | 24 ++-- 3 files changed, 121 insertions(+), 97 deletions(-) diff --git a/src/platform/macos/events_loop.rs b/src/platform/macos/events_loop.rs index bed3df567..c11321cde 100644 --- a/src/platform/macos/events_loop.rs +++ b/src/platform/macos/events_loop.rs @@ -2,16 +2,22 @@ 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, + 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 @@ -36,9 +42,74 @@ 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>, +} + + +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, + } + } } @@ -91,17 +162,9 @@ 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), - user_callback: UserCallback { mutex: std::sync::Mutex::new(None) }, + shared: Arc::new(Shared::new()), + modifiers: Modifiers::new(), } } @@ -114,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); @@ -137,13 +200,13 @@ 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(&mut self, mut callback: F) @@ -156,7 +219,7 @@ impl EventsLoop { } // Track whether or not control flow has changed. - let mut control_flow = std::cell::Cell::new(ControlFlow::Continue); + let control_flow = std::cell::Cell::new(ControlFlow::Continue); let mut callback = |event| { if let ControlFlow::Complete = callback(event) { @@ -164,12 +227,12 @@ impl EventsLoop { } }; - self.user_callback.store(&mut callback); + 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::Complete = control_flow.get() { break; } @@ -190,7 +253,7 @@ 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::Complete = control_flow.get() { break; } @@ -198,51 +261,11 @@ impl EventsLoop { } } - self.user_callback.drop(); - } - - // 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; } @@ -268,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 { @@ -280,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 @@ -309,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)) }, @@ -331,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, @@ -375,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 }, diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index 0e1451b68..dbde5ee94 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -3,6 +3,7 @@ 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 e4b1f1f4a..8ea04a76a 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 { From 0af3c04900e1fd3c252d94084f4897020fbe9cfb Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Fri, 9 Jun 2017 22:55:32 +1000 Subject: [PATCH 14/20] Update api transition to use ControlFlow --- src/api_transition.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/api_transition.rs b/src/api_transition.rs index db2c617d9..645dc59ea 100644 --- a/src/api_transition.rs +++ b/src/api_transition.rs @@ -9,7 +9,6 @@ macro_rules! gen_api_transition { () => { pub struct EventsLoop { windows: ::std::sync::Arc<::std::sync::Mutex>>>, - interrupted: ::std::sync::atomic::AtomicBool, awakened: ::std::sync::Arc<::std::sync::atomic::AtomicBool>, } @@ -21,16 +20,11 @@ macro_rules! gen_api_transition { pub fn new() -> EventsLoop { EventsLoop { 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) { - 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) { if self.awakened.load(::std::sync::atomic::Ordering::Relaxed) { @@ -49,19 +43,23 @@ 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::Complete = callback(e) { + control_flow = ::ControlFlow::Complete; + } + }); + if let ::ControlFlow::Complete = control_flow { break; } + ::std::thread::sleep_ms(5); } } From 4b42af910bad15e4d6e662311653b69b0c947f67 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Fri, 9 Jun 2017 22:55:48 +1000 Subject: [PATCH 15/20] Make x11 backend take &mut self in poll_events method --- src/platform/linux/x11/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index f18b986cd..35472957e 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -117,7 +117,7 @@ impl EventsLoop { } } - pub fn poll_events(&self, mut callback: F) + pub fn poll_events(&mut self, mut callback: F) where F: FnMut(Event) { let xlib = &self.display.xlib; From cd71271f0d990e016dc15207411e01ed4614f8af Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Fri, 9 Jun 2017 07:33:22 -0700 Subject: [PATCH 16/20] Fix api_transition ControlFlow update compile errors --- src/api_transition.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api_transition.rs b/src/api_transition.rs index 645dc59ea..344488013 100644 --- a/src/api_transition.rs +++ b/src/api_transition.rs @@ -32,7 +32,7 @@ macro_rules! gen_api_transition { callback(::Event::Awakened); } - let mut windows = self.windows.lock().unwrap(); + let windows = self.windows.lock().unwrap(); for window in windows.iter() { for event in window.poll_events() { callback(::Event::WindowEvent { @@ -44,7 +44,7 @@ macro_rules! gen_api_transition { } pub fn run_forever(&mut self, mut callback: F) - where F: FnMut(::Event) -> ControlFlow, + where F: FnMut(::Event) -> ::ControlFlow, { self.awakened.store(false, ::std::sync::atomic::Ordering::Relaxed); @@ -59,7 +59,7 @@ macro_rules! gen_api_transition { if let ::ControlFlow::Complete = control_flow { break; } - ::std::thread::sleep_ms(5); + ::std::thread::sleep(::std::time::Duration::from_millis(5)); } } @@ -111,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), + windows: ::std::sync::Arc::downgrade(&events_loop.windows), }) } From 24d6f8da49ddf1dc645c7931e34c860c58c09dff Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sat, 10 Jun 2017 13:43:15 +1000 Subject: [PATCH 17/20] Update README to addition of ControlFlow --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7979037a2..a7bd9f4fa 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::Complete }, - _ => () + _ => winit::ControlFlow::Continue, } }); } From df1276d72a2a08c7ec2a4193eead5d9ad111ebd5 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sat, 17 Jun 2017 22:59:56 +1000 Subject: [PATCH 18/20] Fix x11 EventsLoopProxy::wakeup implementation using a dummy, InputOnly window --- src/platform/linux/x11/mod.rs | 39 ++++++++++++++++++++++---------- src/platform/linux/x11/window.rs | 3 +-- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index 35472957e..151c9e0c2 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -38,12 +38,15 @@ pub struct EventsLoop { 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, - root: ffi::Window, + wakeup_dummy_window: ffi::Window, } impl EventsLoop { @@ -80,6 +83,13 @@ 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 { pending_wakeup: Arc::new(AtomicBool::new(false)), display: display, @@ -88,6 +98,7 @@ impl EventsLoop { devices: Mutex::new(HashMap::new()), xi2ext: xi2ext, root: root, + wakeup_dummy_window: wakeup_dummy_window, }; { @@ -113,7 +124,7 @@ impl EventsLoop { EventsLoopProxy { pending_wakeup: Arc::downgrade(&self.pending_wakeup), display: Arc::downgrade(&self.display), - root: self.root, + wakeup_dummy_window: self.wakeup_dummy_window, } } @@ -152,11 +163,6 @@ impl EventsLoop { let mut control_flow = ControlFlow::Continue; - if self.pending_wakeup.load(atomic::Ordering::Relaxed) { - self.pending_wakeup.store(false, atomic::Ordering::Relaxed); - control_flow = callback(Event::Awakened); - } - // Track whether or not `Complete` was returned when processing the event. { let mut cb = |event| { @@ -204,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::Awakened) + if self.pending_wakeup.load(atomic::Ordering::Relaxed) { + self.pending_wakeup.store(false, atomic::Ordering::Relaxed); + callback(Event::Awakened); + } } } @@ -546,10 +554,14 @@ impl EventsLoopProxy { _ => return Err(EventsLoopClosed), }; - // Push an event on the X event queue so that methods like run_forever will advance. + // 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.root, + window: self.wakeup_dummy_window, format: 32, message_type: 0, serial: 0, @@ -559,7 +571,10 @@ impl EventsLoopProxy { }; unsafe { - (display.xlib.XSendEvent)(display.display, self.root, 0, 0, mem::transmute(&mut xev)); + 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"); } diff --git a/src/platform/linux/x11/window.rs b/src/platform/linux/x11/window.rs index 475cab8af..f5fa67943 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 = { From 04ccad1dbcca13cad951dc966d84bd221d61308e Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 20 Jun 2017 21:25:53 +1000 Subject: [PATCH 19/20] Rename ControlFlow variant from Complete to Break --- examples/cursor.rs | 2 +- examples/fullscreen.rs | 4 ++-- examples/grabbing.rs | 2 +- examples/min_max_size.rs | 2 +- examples/multiwindow.rs | 2 +- examples/proxy.rs | 2 +- examples/transparent.rs | 2 +- examples/window.rs | 2 +- src/api_transition.rs | 6 +++--- src/lib.rs | 10 +++++----- src/platform/linux/wayland/event_loop.rs | 6 +++--- src/platform/linux/x11/mod.rs | 8 ++++---- src/platform/macos/events_loop.rs | 8 ++++---- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/examples/cursor.rs b/examples/cursor.rs index cd979b994..bc7deea6d 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -23,7 +23,7 @@ fn main() { } }, Event::WindowEvent { event: WindowEvent::Closed, .. } => { - return ControlFlow::Complete; + return ControlFlow::Break; }, _ => () } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 921caa612..6f2f118ab 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -37,10 +37,10 @@ fn main() { match event { Event::WindowEvent { event, .. } => { match event { - WindowEvent::Closed => return ControlFlow::Complete, + WindowEvent::Closed => return ControlFlow::Break, WindowEvent::KeyboardInput { input: winit::KeyboardInput { virtual_keycode: Some(winit::VirtualKeyCode::Escape), .. }, .. - } => return ControlFlow::Complete, + } => return ControlFlow::Break, _ => () } }, diff --git a/examples/grabbing.rs b/examples/grabbing.rs index fc6b7d7c3..f7e7c9b3f 100644 --- a/examples/grabbing.rs +++ b/examples/grabbing.rs @@ -28,7 +28,7 @@ fn main() { } }, - WindowEvent::Closed => return ControlFlow::Complete, + WindowEvent::Closed => return ControlFlow::Break, a @ WindowEvent::MouseMoved { .. } => { println!("{:?}", a); diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index 54f03da70..7500e8935 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -13,7 +13,7 @@ fn main() { println!("{:?}", event); match event { - winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => winit::ControlFlow::Complete, + winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => winit::ControlFlow::Break, _ => winit::ControlFlow::Continue, } }); diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 28f9955f2..9c5b93b82 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -24,7 +24,7 @@ fn main() { num_windows -= 1; if num_windows == 0 { - return winit::ControlFlow::Complete; + return winit::ControlFlow::Break; } }, _ => (), diff --git a/examples/proxy.rs b/examples/proxy.rs index 8741d7751..338c4675b 100644 --- a/examples/proxy.rs +++ b/examples/proxy.rs @@ -22,7 +22,7 @@ fn main() { println!("{:?}", event); match event { winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => - winit::ControlFlow::Complete, + winit::ControlFlow::Break, _ => winit::ControlFlow::Continue, } }); diff --git a/examples/transparent.rs b/examples/transparent.rs index da9a80e72..de6331757 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -13,7 +13,7 @@ fn main() { println!("{:?}", event); match event { - winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => winit::ControlFlow::Complete, + winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => winit::ControlFlow::Break, _ => winit::ControlFlow::Continue, } }); diff --git a/examples/window.rs b/examples/window.rs index fd5ef6e9b..65f0ade2b 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -13,7 +13,7 @@ fn main() { match event { winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => { - winit::ControlFlow::Complete + winit::ControlFlow::Break }, _ => winit::ControlFlow::Continue, } diff --git a/src/api_transition.rs b/src/api_transition.rs index 344488013..158c6d44d 100644 --- a/src/api_transition.rs +++ b/src/api_transition.rs @@ -52,11 +52,11 @@ macro_rules! gen_api_transition { loop { let mut control_flow = ::ControlFlow::Continue; self.poll_events(|e| { - if let ::ControlFlow::Complete = callback(e) { - control_flow = ::ControlFlow::Complete; + if let ::ControlFlow::Break = callback(e) { + control_flow = ::ControlFlow::Break; } }); - if let ::ControlFlow::Complete = control_flow { + if let ::ControlFlow::Break = control_flow { break; } ::std::thread::sleep(::std::time::Duration::from_millis(5)); diff --git a/src/lib.rs b/src/lib.rs index 74d3c5a87..d4b91c40c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,7 @@ //! ``` //! //! The second way is to call `events_loop.run_forever(...)`. As its name tells, it will run -//! forever unless it is stopped by returning `ControlFlow::Complete`. +//! forever unless it is stopped by returning `ControlFlow::Break`. //! //! ```no_run //! use winit::{ControlFlow, Event, WindowEvent}; @@ -62,7 +62,7 @@ //! match event { //! Event::WindowEvent { event: WindowEvent::Closed, .. } => { //! println!("The window was closed ; stopping"); -//! ControlFlow::Complete +//! ControlFlow::Break //! }, //! _ => ControlFlow::Continue, //! } @@ -143,7 +143,7 @@ pub mod os; /// events_loop.run_forever(|event| { /// match event { /// Event::WindowEvent { event: WindowEvent::Closed, .. } => { -/// ControlFlow::Complete +/// ControlFlow::Break /// }, /// _ => ControlFlow::Continue, /// } @@ -192,8 +192,8 @@ pub struct EventsLoop { pub enum ControlFlow { /// Continue looping and waiting for events. Continue, - /// Exit from the event loop. - Complete, + /// Break from the event loop. + Break, } impl EventsLoop { diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index f2b1325e1..703562b6a 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -226,8 +226,8 @@ impl EventsLoop { // Check for control flow by wrapping the callback. let control_flow = ::std::cell::Cell::new(ControlFlow::Continue); - let callback = |event| if let ControlFlow::Complete = callback(event) { - control_flow.set(ControlFlow::Complete); + let callback = |event| if let ControlFlow::Break = callback(event) { + control_flow.set(ControlFlow::Break); }; // set the callback into the sink @@ -251,7 +251,7 @@ impl EventsLoop { self.prune_dead_windows() } - if let ControlFlow::Complete = control_flow.get() { + if let ControlFlow::Break = control_flow.get() { break; } } diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index 151c9e0c2..7b8f6bd7b 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -163,18 +163,18 @@ impl EventsLoop { let mut control_flow = ControlFlow::Continue; - // Track whether or not `Complete` was returned when processing the event. + // Track whether or not `Break` was returned when processing the event. { let mut cb = |event| { - if let ControlFlow::Complete = callback(event) { - control_flow = ControlFlow::Complete; + if let ControlFlow::Break = callback(event) { + control_flow = ControlFlow::Break; } }; self.process_event(&mut xev, &mut cb); } - if let ControlFlow::Complete = control_flow { + if let ControlFlow::Break = control_flow { break; } } diff --git a/src/platform/macos/events_loop.rs b/src/platform/macos/events_loop.rs index c11321cde..52130bbe6 100644 --- a/src/platform/macos/events_loop.rs +++ b/src/platform/macos/events_loop.rs @@ -222,8 +222,8 @@ impl EventsLoop { let control_flow = std::cell::Cell::new(ControlFlow::Continue); let mut callback = |event| { - if let ControlFlow::Complete = callback(event) { - control_flow.set(ControlFlow::Complete); + if let ControlFlow::Break = callback(event) { + control_flow.set(ControlFlow::Break); } }; @@ -233,7 +233,7 @@ impl EventsLoop { unsafe { // First, yield all pending events. self.shared.call_user_callback_with_pending_events(); - if let ControlFlow::Complete = control_flow.get() { + if let ControlFlow::Break = control_flow.get() { break; } @@ -254,7 +254,7 @@ impl EventsLoop { if let Some(event) = maybe_event { self.shared.user_callback.call_with_event(event); - if let ControlFlow::Complete = control_flow.get() { + if let ControlFlow::Break = control_flow.get() { break; } } From fe61d81d4109f38f6563165eab962ad14ea4d250 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 20 Jun 2017 21:34:00 +1000 Subject: [PATCH 20/20] Change Complete to Break in the README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7bd9f4fa..868486def 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ fn main() { events_loop.run_forever(|event| { match event { winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => { - winit::ControlFlow::Complete + winit::ControlFlow::Break }, _ => winit::ControlFlow::Continue, }