Skip to content

Commit

Permalink
[WIP] Have EventsLoopProxy::wakeup return a Result. Begin linux impl.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mitchmindtree committed May 25, 2017
1 parent c8e791b commit f6587ae
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 34 deletions.
2 changes: 1 addition & 1 deletion examples/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
});

Expand Down
3 changes: 1 addition & 2 deletions src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
37 changes: 28 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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`"
}
}

Expand Down
25 changes: 22 additions & 3 deletions src/platform/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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(),
Expand All @@ -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(),
}
}
}
70 changes: 61 additions & 9 deletions src/platform/linux/wayland/event_loop.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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 {
Expand All @@ -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())
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();

Expand All @@ -173,15 +214,15 @@ impl EventsLoop {
unsafe { sink_guard.set_callback(old_cb) };
}

if self.cleanup_needed.swap(false, ::std::sync::atomic::Ordering::Relaxed) {
if self.cleanup_needed.swap(false, atomic::Ordering::Relaxed) {
self.prune_dead_windows()
}
}

pub fn run_forever<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();
Expand All @@ -195,23 +236,34 @@ 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()
}
}

// 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 {
Expand Down
2 changes: 1 addition & 1 deletion src/platform/linux/wayland/mod.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down
Loading

0 comments on commit f6587ae

Please sign in to comment.