[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.
This commit is contained in:
mitchmindtree 2017-05-25 23:19:13 +10:00
parent c8e791b402
commit f6587aed39
7 changed files with 149 additions and 34 deletions

View file

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

View file

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

View file

@ -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<platform::EventsLoop>,
}
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`"
}
}

View file

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

View file

@ -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<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);
// 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<Mutex<EventQueue>>, Arc<AtomicBool>) {
(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<F>(&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<FnMut(_)>) };
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 {

View file

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

View file

@ -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<XConnection>,
wm_delete_window: ffi::Atom,
windows: Mutex<HashMap<WindowId, WindowData>>,
devices: Mutex<HashMap<DeviceId, Device>>,
xi2ext: XExtension,
pending_wakeup: Arc<AtomicBool>,
root: ffi::Window,
}
pub struct EventsLoopProxy {
pending_wakeup: Weak<AtomicBool>,
}
impl EventsLoop {
pub fn new(display: Arc<XConnection>) -> 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<F>(&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<F>(&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,