Implement Poll and WaitUntil in the stdweb backend

This commit is contained in:
Ryan Goldstein 2019-06-16 21:30:05 -07:00
parent b59e3c670b
commit 2690306f4a
5 changed files with 115 additions and 45 deletions

View file

@ -14,6 +14,7 @@ categories = ["gui"]
features = ["serde"] features = ["serde"]
[dependencies] [dependencies]
instant = "0.1"
lazy_static = "1" lazy_static = "1"
libc = "0.2" libc = "0.2"
log = "0.4" log = "0.4"
@ -73,6 +74,6 @@ percent-encoding = "1.0"
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "windows"))'.dependencies.parking_lot] [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "windows"))'.dependencies.parking_lot]
version = "0.8" version = "0.8"
[target.'cfg(target_arch = "wasm32")'.dependencies.stdweb] [target.'cfg(target_arch = "wasm32")'.dependencies]
path = "../stdweb" stdweb = { path = "../stdweb", optional = true }
optional = true instant = { version = "0.1", features = ["stdweb"] }

View file

@ -4,7 +4,7 @@
//! processed and used to modify the program state. For more details, see the root-level documentation. //! processed and used to modify the program state. For more details, see the root-level documentation.
//! //!
//! [event_loop_run]: ../event_loop/struct.EventLoop.html#method.run //! [event_loop_run]: ../event_loop/struct.EventLoop.html#method.run
use std::time::Instant; use instant::Instant;
use std::path::PathBuf; use std::path::PathBuf;
use dpi::{LogicalPosition, LogicalSize}; use dpi::{LogicalPosition, LogicalSize};
@ -58,7 +58,7 @@ impl<T> Event<T> {
} }
/// Describes the reason the event loop is resuming. /// Describes the reason the event loop is resuming.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StartCause { pub enum StartCause {
/// Sent if the time specified by `ControlFlow::WaitUntil` has been reached. Contains the /// Sent if the time specified by `ControlFlow::WaitUntil` has been reached. Contains the
/// moment the timeout was requested and the requested resume time. The actual resume time is /// moment the timeout was requested and the requested resume time. The actual resume time is

View file

@ -10,7 +10,7 @@
//! [event_loop_proxy]: ./struct.EventLoopProxy.html //! [event_loop_proxy]: ./struct.EventLoopProxy.html
//! [send_event]: ./struct.EventLoopProxy.html#method.send_event //! [send_event]: ./struct.EventLoopProxy.html#method.send_event
use std::{fmt, error}; use std::{fmt, error};
use std::time::Instant; use instant::Instant;
use std::ops::Deref; use std::ops::Deref;
use platform_impl; use platform_impl;
@ -69,7 +69,7 @@ impl<T> fmt::Debug for EventLoopWindowTarget<T> {
/// the control flow to `Poll`. /// the control flow to `Poll`.
/// ///
/// [events_cleared]: ../event/enum.Event.html#variant.EventsCleared /// [events_cleared]: ../event/enum.Event.html#variant.EventsCleared
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ControlFlow { pub enum ControlFlow {
/// When the current loop iteration finishes, immediately begin a new iteration regardless of /// When the current loop iteration finishes, immediately begin a new iteration regardless of
/// whether or not new events are available to process. /// whether or not new events are available to process.

View file

@ -75,6 +75,7 @@
//! [`LoopDestroyed`]: ./event/enum.Event.html#variant.LoopDestroyed //! [`LoopDestroyed`]: ./event/enum.Event.html#variant.LoopDestroyed
//! [`platform`]: ./platform/index.html //! [`platform`]: ./platform/index.html
extern crate instant;
#[allow(unused_imports)] #[allow(unused_imports)]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;

View file

@ -3,6 +3,7 @@ use super::*;
use dpi::LogicalPosition; use dpi::LogicalPosition;
use event::{DeviceId as RootDI, ElementState, Event, KeyboardInput, MouseScrollDelta, StartCause, TouchPhase, WindowEvent}; use event::{DeviceId as RootDI, ElementState, Event, KeyboardInput, MouseScrollDelta, StartCause, TouchPhase, WindowEvent};
use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed};
use instant::{Duration, Instant};
use window::{WindowId as RootWI}; use window::{WindowId as RootWI};
use stdweb::{ use stdweb::{
traits::*, traits::*,
@ -10,15 +11,17 @@ use stdweb::{
document, document,
event::*, event::*,
html_element::CanvasElement, html_element::CanvasElement,
window,
TimeoutHandle, TimeoutHandle,
}, },
}; };
use std::cell::RefCell; use std::{
use std::collections::VecDeque; cell::RefCell,
use std::collections::vec_deque::IntoIter as VecDequeIter; collections::{VecDeque, vec_deque::IntoIter as VecDequeIter},
use std::marker::PhantomData; clone::Clone,
use std::rc::Rc; marker::PhantomData,
use std::time::Instant; rc::Rc,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(i32); pub struct DeviceId(i32);
@ -40,31 +43,37 @@ pub struct EventLoopWindowTarget<T: 'static> {
impl<T> EventLoopWindowTarget<T> { impl<T> EventLoopWindowTarget<T> {
fn new() -> Self { fn new() -> Self {
EventLoopWindowTarget { EventLoopWindowTarget {
runner: Rc::new(ELRShared { runner: EventLoopRunnerShared(Rc::new(ELRShared {
runner: RefCell::new(None), runner: RefCell::new(None),
events: RefCell::new(VecDeque::new()) events: RefCell::new(VecDeque::new())
}) }))
} }
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct EventLoopProxy<T> { pub struct EventLoopProxy<T: 'static> {
runner: EventLoopRunnerShared<T> runner: EventLoopRunnerShared<T>
} }
impl<T> EventLoopProxy<T> { impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
self.runner.send_event(Event::UserEvent(event)); self.runner.send_event(Event::UserEvent(event));
Ok(()) Ok(())
} }
} }
pub type EventLoopRunnerShared<T> = Rc<ELRShared<T>>; pub struct EventLoopRunnerShared<T>(Rc<ELRShared<T>>);
impl<T> Clone for EventLoopRunnerShared<T> {
fn clone(&self) -> Self {
EventLoopRunnerShared(self.0.clone())
}
}
pub struct ELRShared<T> { pub struct ELRShared<T> {
runner: RefCell<Option<EventLoopRunner<T>>>, runner: RefCell<Option<EventLoopRunner<T>>>,
events: RefCell<VecDeque<Event<T>>>, // TODO: this may not be necessary? events: RefCell<VecDeque<Event<T>>>,
} }
struct EventLoopRunner<T> { struct EventLoopRunner<T> {
@ -74,6 +83,7 @@ struct EventLoopRunner<T> {
} }
enum ControlFlowStatus { enum ControlFlowStatus {
Init,
WaitUntil { WaitUntil {
timeout: TimeoutHandle, timeout: TimeoutHandle,
start: Instant, start: Instant,
@ -91,6 +101,7 @@ enum ControlFlowStatus {
impl ControlFlowStatus { impl ControlFlowStatus {
fn to_control_flow(&self) -> ControlFlow { fn to_control_flow(&self) -> ControlFlow {
match self { match self {
ControlFlowStatus::Init => ControlFlow::Poll, // During the Init loop, the user should get Poll, the default control value
ControlFlowStatus::WaitUntil { end, .. } => ControlFlow::WaitUntil(*end), ControlFlowStatus::WaitUntil { end, .. } => ControlFlow::WaitUntil(*end),
ControlFlowStatus::Wait { .. } => ControlFlow::Wait, ControlFlowStatus::Wait { .. } => ControlFlow::Wait,
ControlFlowStatus::Poll { .. } => ControlFlow::Poll, ControlFlowStatus::Poll { .. } => ControlFlow::Poll,
@ -286,7 +297,7 @@ fn add_event<T: 'static, E, F>(elrs: &EventLoopRunnerShared<T>, target: &impl IE
target.add_event_listener(move |event: E| { target.add_event_listener(move |event: E| {
// Don't capture the event if the events loop has been destroyed // Don't capture the event if the events loop has been destroyed
match &*elrs.runner.borrow() { match &*elrs.0.runner.borrow() {
Some(ref runner) if runner.control.is_exit() => return, Some(ref runner) if runner.control.is_exit() => return,
_ => () _ => ()
} }
@ -299,17 +310,17 @@ fn add_event<T: 'static, E, F>(elrs: &EventLoopRunnerShared<T>, target: &impl IE
}); });
} }
impl<T> ELRShared<T> { impl<T: 'static> EventLoopRunnerShared<T> {
// Set the event callback to use for the event loop runner // Set the event callback to use for the event loop runner
// This the event callback is a fairly thin layer over the user-provided callback that closes // This the event callback is a fairly thin layer over the user-provided callback that closes
// over a RootEventLoopWindowTarget reference // over a RootEventLoopWindowTarget reference
fn set_listener(&self, event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>) { fn set_listener(&self, event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>) {
// TODO: Start the poll here *self.0.runner.borrow_mut() = Some(EventLoopRunner {
*self.runner.borrow_mut() = Some(EventLoopRunner { control: ControlFlowStatus::Init,
control: ControlFlowStatus::Exit,
is_busy: false, is_busy: false,
event_handler, event_handler,
}); });
self.send_event(Event::NewEvents(StartCause::Init));
} }
// Add an event to the event loop runner // Add an event to the event loop runner
@ -321,23 +332,46 @@ impl<T> ELRShared<T> {
return; return;
} }
// TODO: Determine if a timeout needs to be cleared
let start_cause = StartCause::Poll; // TODO: determine start cause
// Determine if event handling is in process, and then release the borrow on the runner // Determine if event handling is in process, and then release the borrow on the runner
match *self.runner.borrow() { match *self.0.runner.borrow() {
Some(ref runner) if !runner.is_busy => { Some(ref runner) if !runner.is_busy => {
let (start_cause, event_is_start) = if let Event::NewEvents(cause) = event {
(cause, true)
} else {
(match runner.control {
ControlFlowStatus::Init => StartCause::Init,
ControlFlowStatus::Poll { ref timeout } => {
timeout.clear();
StartCause::Poll
}
ControlFlowStatus::Wait { start } => StartCause::WaitCancelled {
start,
requested_resume: None,
},
ControlFlowStatus::WaitUntil { start, end, ref timeout } => {
timeout.clear();
StartCause::WaitCancelled {
start,
requested_resume: Some(end)
}
},
ControlFlowStatus::Exit => { return; }
}, false)
};
let mut control = runner.control.to_control_flow(); let mut control = runner.control.to_control_flow();
// Handle starting a new batch of events // Handle starting a new batch of events
// //
// The user is informed via Event::NewEvents that there is a batch of events to process // The user is informed via Event::NewEvents that there is a batch of events to process
// However, there is only one of these per batch of events // However, there is only one of these per batch of events
self.handle_event(Event::NewEvents(start_cause), &mut control); self.handle_event(Event::NewEvents(start_cause), &mut control);
if !event_is_start {
self.handle_event(event, &mut control); self.handle_event(event, &mut control);
}
self.handle_event(Event::EventsCleared, &mut control); self.handle_event(Event::EventsCleared, &mut control);
// TODO: integrate control flow change and set up the next iteration self.apply_control_flow(control);
// If the event loop is closed, it has been closed this iteration and now the closing // If the event loop is closed, it has been closed this iteration and now the closing
// event should be emitted // event should be emitted
@ -346,31 +380,22 @@ impl<T> ELRShared<T> {
} }
} }
_ => { _ => {
self.events.borrow_mut().push_back(event); self.0.events.borrow_mut().push_back(event);
} }
} }
} }
// Check if the event loop is currntly closed
fn closed(&self) -> bool {
match *self.runner.borrow() {
Some(ref runner) => runner.control.is_exit(),
None => false, // If the event loop is None, it has not been intialised yet, so it cannot be closed
}
}
// handle_event takes in events and either queues them or applies a callback // handle_event takes in events and either queues them or applies a callback
// //
// It should only ever be called from send_event // It should only ever be called from send_event
fn handle_event(&self, event: Event<T>, control: &mut ControlFlow) { fn handle_event(&self, event: Event<T>, control: &mut ControlFlow) {
let closed = self.closed(); let closed = self.closed();
match *self.runner.borrow_mut() { match *self.0.runner.borrow_mut() {
Some(ref mut runner) => { Some(ref mut runner) => {
// An event is being processed, so the runner should be marked busy // An event is being processed, so the runner should be marked busy
runner.is_busy = true; runner.is_busy = true;
// TODO: bracket this in control flow events?
(runner.event_handler)(event, control); (runner.event_handler)(event, control);
// Maintain closed state, even if the callback changes it // Maintain closed state, even if the callback changes it
@ -383,18 +408,61 @@ impl<T> ELRShared<T> {
} }
// If an event is being handled without a runner somehow, add it to the event queue so // If an event is being handled without a runner somehow, add it to the event queue so
// it will eventually be processed // it will eventually be processed
_ => self.events.borrow_mut().push_back(event) _ => self.0.events.borrow_mut().push_back(event)
} }
// Don't take events out of the queue if the loop is closed or the runner doesn't exist // Don't take events out of the queue if the loop is closed or the runner doesn't exist
// If the runner doesn't exist and this method recurses, it will recurse infinitely // If the runner doesn't exist and this method recurses, it will recurse infinitely
if !closed && self.runner.borrow().is_some() { if !closed && self.0.runner.borrow().is_some() {
// Take an event out of the queue and handle it // Take an event out of the queue and handle it
if let Some(event) = self.events.borrow_mut().pop_front() { if let Some(event) = self.0.events.borrow_mut().pop_front() {
self.handle_event(event, control); self.handle_event(event, control);
} }
} }
} }
// Apply the new ControlFlow that has been selected by the user
// Start any necessary timeouts etc
fn apply_control_flow(&self, control_flow: ControlFlow) {
let control_flow_status = match control_flow {
ControlFlow::Poll => {
let cloned = self.clone();
ControlFlowStatus::Poll {
timeout: window().set_clearable_timeout(move || cloned.send_event(Event::NewEvents(StartCause::Poll)), 0)
}
}
ControlFlow::Wait => ControlFlowStatus::Wait { start: Instant::now() },
ControlFlow::WaitUntil(end) => {
let cloned = self.clone();
let start = Instant::now();
let delay = if end <= start {
Duration::from_millis(0)
} else {
end - start
};
ControlFlowStatus::WaitUntil {
start,
end,
timeout: window().set_clearable_timeout(move || cloned.send_event(Event::NewEvents(StartCause::Poll)), delay.as_millis() as u32)
}
}
ControlFlow::Exit => ControlFlowStatus::Exit,
};
match *self.0.runner.borrow_mut() {
Some(ref mut runner) => {
runner.control = control_flow_status;
}
None => ()
}
}
// Check if the event loop is currntly closed
fn closed(&self) -> bool {
match *self.0.runner.borrow() {
Some(ref runner) => runner.control.is_exit(),
None => false, // If the event loop is None, it has not been intialised yet, so it cannot be closed
}
}
} }