Wayland: rework the event loop & expose readiness signal (#298)
* wayland: don't create a second event_queue As each EventsLoop has its own context, this is no longer necessary. * wayland: buffer events rather than direct dispatch Changes the behavior of the event loop to first internally buffer the events generated by the wayland handlers, and then dispatch them to the client's closure. - It simplifies the event loop logic - It makes it possible for the user to call window methods such as `set_title()` or `set_inner_size()` without causing a deadlock * wayland: add is_ready() & fix protocol errors Adds a `is_ready()` method to the windows to advertize when it is legal to start drawing, and fix a few wayland protocol mishandling in the process.
This commit is contained in:
parent
df7e349c70
commit
515595153d
6 changed files with 130 additions and 141 deletions
|
|
@ -26,19 +26,15 @@ use super::keyboard::KbdHandler;
|
|||
///
|
||||
/// Failure to do so is unsafe™
|
||||
pub struct EventsLoopSink {
|
||||
callback: Box<FnMut(::Event)>
|
||||
buffer: VecDeque<::Event>
|
||||
}
|
||||
|
||||
unsafe impl Send for EventsLoopSink { }
|
||||
|
||||
impl EventsLoopSink {
|
||||
pub fn new() -> (EventsLoopSink, Arc<Mutex<VecDeque<::Event>>>) {
|
||||
let buffer = Arc::new(Mutex::new(VecDeque::new()));
|
||||
let buffer_clone = buffer.clone();
|
||||
let sink = EventsLoopSink {
|
||||
callback: Box::new(move |evt| { println!("TEMP: {:?}", evt); buffer.lock().unwrap().push_back(evt)}),
|
||||
};
|
||||
(sink, buffer_clone)
|
||||
pub fn new() -> EventsLoopSink{EventsLoopSink {
|
||||
buffer: VecDeque::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_event(&mut self, evt: ::WindowEvent, wid: WindowId) {
|
||||
|
|
@ -46,37 +42,27 @@ impl EventsLoopSink {
|
|||
event: evt,
|
||||
window_id: ::WindowId(::platform::WindowId::Wayland(wid))
|
||||
};
|
||||
(self.callback)(evt)
|
||||
self.buffer.push_back(evt);
|
||||
}
|
||||
|
||||
// This function is only safe of the set callback is unset before exclusive
|
||||
// access to the wayland EventQueue is finished.
|
||||
//
|
||||
// The callback also cannot be used any longer as long as it has not been
|
||||
// cleared from the Sink.
|
||||
unsafe fn set_callback(&mut self, cb: Box<FnMut(::Event)>) -> Box<FnMut(::Event)> {
|
||||
::std::mem::replace(&mut self.callback, cb)
|
||||
pub fn send_raw_event(&mut self, evt: ::Event) {
|
||||
self.buffer.push_back(evt);
|
||||
}
|
||||
|
||||
fn with_callback<F>(&mut self, f: F)
|
||||
where F: FnOnce(&mut FnMut(::Event)),
|
||||
{
|
||||
f(&mut *self.callback)
|
||||
fn empty_with<F>(&mut self, callback: &mut F) where F: FnMut(::Event) {
|
||||
for evt in self.buffer.drain(..) {
|
||||
callback(evt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventsLoop {
|
||||
// the global wayland context
|
||||
// the wayland context
|
||||
ctxt: Arc<WaylandContext>,
|
||||
// our EventQueue
|
||||
evq: Arc<Mutex<EventQueue>>,
|
||||
// ids of the DecoratedHandlers of the surfaces we know
|
||||
decorated_ids: Mutex<Vec<(usize, Arc<wl_surface::WlSurface>)>>,
|
||||
// our sink, receiver of callbacks, shared with some handlers
|
||||
// our sink, shared with some handlers, buffering the events
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
// a buffer in which events that were dispatched internally are stored
|
||||
// until the user next dispatches events
|
||||
buffer: Arc<Mutex<VecDeque<::Event>>>,
|
||||
// trigger cleanup of the dead surfaces
|
||||
cleanup_needed: Arc<AtomicBool>,
|
||||
// Whether or not there is a pending `Awakened` event to be emitted.
|
||||
|
|
@ -114,17 +100,16 @@ impl EventsLoopProxy {
|
|||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new(ctxt: Arc<WaylandContext>) -> EventsLoop {
|
||||
let mut evq = ctxt.display.create_event_queue();
|
||||
let (sink, buffer) = EventsLoopSink::new();
|
||||
let sink = Arc::new(Mutex::new(sink));
|
||||
let hid = evq.add_handler_with_init(InputHandler::new(&ctxt, sink.clone()));
|
||||
pub fn new(mut ctxt: WaylandContext) -> EventsLoop {
|
||||
let sink = Arc::new(Mutex::new(EventsLoopSink::new()));
|
||||
let inputh = InputHandler::new(&ctxt, sink.clone());
|
||||
let hid = ctxt.evq.get_mut().unwrap().add_handler_with_init(inputh);
|
||||
let ctxt = Arc::new(ctxt);
|
||||
|
||||
EventsLoop {
|
||||
ctxt: ctxt,
|
||||
evq: Arc::new(Mutex::new(evq)),
|
||||
decorated_ids: Mutex::new(Vec::new()),
|
||||
sink: sink,
|
||||
buffer: buffer,
|
||||
pending_wakeup: Arc::new(AtomicBool::new(false)),
|
||||
cleanup_needed: Arc::new(AtomicBool::new(false)),
|
||||
hid: hid
|
||||
|
|
@ -144,41 +129,39 @@ impl EventsLoop {
|
|||
}
|
||||
|
||||
// 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())
|
||||
pub fn get_window_init(&self) -> Arc<AtomicBool> {
|
||||
self.cleanup_needed.clone()
|
||||
}
|
||||
|
||||
pub fn register_window(&self, decorated_id: usize, surface: Arc<wl_surface::WlSurface>) {
|
||||
self.buffer.lock().unwrap().push_back(::Event::WindowEvent {
|
||||
window_id: ::WindowId(::platform::WindowId::Wayland(make_wid(&surface))),
|
||||
event: ::WindowEvent::Refresh
|
||||
});
|
||||
self.decorated_ids.lock().unwrap().push((decorated_id, surface.clone()));
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let mut guard = self.ctxt.evq.lock().unwrap();
|
||||
let mut state = guard.state();
|
||||
state.get_mut_handler::<InputHandler>(self.hid).windows.push(surface);
|
||||
}
|
||||
|
||||
fn process_resize(evq: &mut EventQueue, ids: &[(usize, Arc<wl_surface::WlSurface>)], callback: &mut FnMut(::Event))
|
||||
fn process_resize(evq: &mut EventQueue, ids: &[(usize, Arc<wl_surface::WlSurface>)], sink: &mut EventsLoopSink)
|
||||
{
|
||||
let mut state = evq.state();
|
||||
for &(decorated_id, ref window) in ids {
|
||||
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(decorated_id);
|
||||
if let Some((w, h)) = decorated.handler().as_mut().and_then(|h| h.take_newsize()) {
|
||||
decorated.resize(w as i32, h as i32);
|
||||
callback(
|
||||
::Event::WindowEvent {
|
||||
window_id: ::WindowId(::platform::WindowId::Wayland(make_wid(&window))),
|
||||
event: ::WindowEvent::Resized(w,h)
|
||||
}
|
||||
sink.send_event(
|
||||
::WindowEvent::Resized(w,h),
|
||||
make_wid(&window)
|
||||
);
|
||||
}
|
||||
if decorated.handler().as_mut().map(|h| h.take_refresh()).unwrap_or(false) {
|
||||
sink.send_event(
|
||||
::WindowEvent::Refresh,
|
||||
make_wid(&window)
|
||||
);
|
||||
}
|
||||
if decorated.handler().as_ref().map(|h| h.is_closed()).unwrap_or(false) {
|
||||
callback(
|
||||
::Event::WindowEvent {
|
||||
window_id: ::WindowId(::platform::WindowId::Wayland(make_wid(&window))),
|
||||
event: ::WindowEvent::Closed
|
||||
}
|
||||
sink.send_event(
|
||||
::WindowEvent::Closed,
|
||||
make_wid(&window)
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -187,7 +170,7 @@ impl EventsLoop {
|
|||
|
||||
fn prune_dead_windows(&self) {
|
||||
self.decorated_ids.lock().unwrap().retain(|&(_, ref w)| w.status() == Liveness::Alive);
|
||||
let mut evq_guard = self.evq.lock().unwrap();
|
||||
let mut evq_guard = self.ctxt.evq.lock().unwrap();
|
||||
let mut state = evq_guard.state();
|
||||
let handler = state.get_mut_handler::<InputHandler>(self.hid);
|
||||
handler.windows.retain(|w| w.status() == Liveness::Alive);
|
||||
|
|
@ -198,11 +181,12 @@ impl EventsLoop {
|
|||
}
|
||||
}
|
||||
|
||||
fn empty_buffer<F>(&self, callback: &mut F) where F: FnMut(::Event) {
|
||||
let mut guard = self.buffer.lock().unwrap();
|
||||
for evt in guard.drain(..) {
|
||||
callback(evt)
|
||||
}
|
||||
fn post_dispatch_triggers(&self) {
|
||||
let mut evq_guard = self.ctxt.evq.lock().unwrap();
|
||||
let mut sink_guard = self.sink.lock().unwrap();
|
||||
let ids_guard = self.decorated_ids.lock().unwrap();
|
||||
self.emit_pending_wakeup(&mut sink_guard);
|
||||
Self::process_resize(&mut evq_guard, &ids_guard, &mut sink_guard);
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
|
|
@ -211,41 +195,15 @@ impl EventsLoop {
|
|||
// 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();
|
||||
// dispatch any pre-buffered events
|
||||
self.sink.lock().unwrap().empty_with(&mut callback);
|
||||
|
||||
// dispatch pre-buffered events
|
||||
self.empty_buffer(&mut callback);
|
||||
|
||||
// read some events from the socket if some are waiting & queue is empty
|
||||
if let Some(guard) = evq_guard.prepare_read() {
|
||||
guard.read_events().expect("Wayland connection unexpectedly lost");
|
||||
}
|
||||
|
||||
// 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<FnMut(_)>) };
|
||||
let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) };
|
||||
|
||||
// then do the actual dispatching
|
||||
// try dispatching events without blocking
|
||||
self.ctxt.read_events();
|
||||
self.ctxt.dispatch_pending();
|
||||
evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
|
||||
self.post_dispatch_triggers();
|
||||
|
||||
self.emit_pending_wakeup();
|
||||
|
||||
{
|
||||
let mut sink_guard = self.sink.lock().unwrap();
|
||||
|
||||
// events where probably dispatched, process resize
|
||||
let ids_guard = self.decorated_ids.lock().unwrap();
|
||||
sink_guard.with_callback(
|
||||
|cb| Self::process_resize(&mut evq_guard, &ids_guard, cb)
|
||||
);
|
||||
|
||||
// replace the old noop callback
|
||||
unsafe { sink_guard.set_callback(old_cb) };
|
||||
}
|
||||
self.sink.lock().unwrap().empty_with(&mut callback);
|
||||
|
||||
if self.cleanup_needed.swap(false, atomic::Ordering::Relaxed) {
|
||||
self.prune_dead_windows()
|
||||
|
|
@ -258,9 +216,6 @@ impl EventsLoop {
|
|||
// 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 mut callback = |event| if let ControlFlow::Break = callback(event) {
|
||||
|
|
@ -268,24 +223,15 @@ impl EventsLoop {
|
|||
};
|
||||
|
||||
// dispatch pre-buffered events
|
||||
self.empty_buffer(&mut callback);
|
||||
|
||||
// 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<FnMut(_)>) };
|
||||
let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) };
|
||||
self.sink.lock().unwrap().empty_with(&mut callback);
|
||||
|
||||
loop {
|
||||
// dispatch events blocking if needed
|
||||
self.ctxt.dispatch();
|
||||
evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
|
||||
self.post_dispatch_triggers();
|
||||
|
||||
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();
|
||||
// empty buffer of events
|
||||
self.sink.lock().unwrap().empty_with(&mut callback);
|
||||
|
||||
if self.cleanup_needed.swap(false, atomic::Ordering::Relaxed) {
|
||||
self.prune_dead_windows()
|
||||
|
|
@ -295,15 +241,12 @@ impl EventsLoop {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// replace the old noop callback
|
||||
unsafe { self.sink.lock().unwrap().set_callback(old_cb) };
|
||||
}
|
||||
|
||||
// If an `EventsLoopProxy` has signalled a wakeup, emit an event and reset the flag.
|
||||
fn emit_pending_wakeup(&self) {
|
||||
fn emit_pending_wakeup(&self, sink: &mut EventsLoopSink) {
|
||||
if self.pending_wakeup.load(atomic::Ordering::Relaxed) {
|
||||
self.sink.lock().unwrap().with_callback(|cb| cb(::Event::Awakened));
|
||||
sink.send_raw_event(::Event::Awakened);
|
||||
self.pending_wakeup.store(false, atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue