Draft web platform structure
This commit is contained in:
parent
eea9530f38
commit
c5703eb00a
26 changed files with 1171 additions and 153 deletions
112
src/platform_impl/web/event_loop/mod.rs
Normal file
112
src/platform_impl/web/event_loop/mod.rs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
mod proxy;
|
||||
mod runner;
|
||||
mod state;
|
||||
mod window_target;
|
||||
|
||||
pub use self::proxy::Proxy;
|
||||
pub use self::window_target::WindowTarget;
|
||||
|
||||
use super::{backend, device, monitor, window};
|
||||
use crate::event::{DeviceId, ElementState, Event, KeyboardInput, WindowEvent};
|
||||
use crate::event_loop as root;
|
||||
use crate::window::WindowId;
|
||||
|
||||
use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
elw: root::EventLoopWindowTarget<T>,
|
||||
}
|
||||
|
||||
impl<T> EventLoop<T> {
|
||||
pub fn new() -> Self {
|
||||
EventLoop {
|
||||
elw: root::EventLoopWindowTarget {
|
||||
p: WindowTarget::new(),
|
||||
_marker: PhantomData,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn available_monitors(&self) -> VecDequeIter<monitor::Handle> {
|
||||
VecDeque::new().into_iter()
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> monitor::Handle {
|
||||
monitor::Handle
|
||||
}
|
||||
|
||||
pub fn run<F>(self, mut event_handler: F) -> !
|
||||
where
|
||||
F: 'static + FnMut(Event<T>, &root::EventLoopWindowTarget<T>, &mut root::ControlFlow),
|
||||
{
|
||||
let target = root::EventLoopWindowTarget {
|
||||
p: self.elw.p.clone(),
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
let runner = self.elw.p.run(Box::new(move |event, flow| {
|
||||
event_handler(event, &target, flow)
|
||||
}));
|
||||
|
||||
backend::Document::on_blur(|| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(window::Id),
|
||||
event: WindowEvent::Focused(false),
|
||||
});
|
||||
});
|
||||
|
||||
backend::Document::on_focus(|| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(window::Id),
|
||||
event: WindowEvent::Focused(true),
|
||||
});
|
||||
});
|
||||
|
||||
backend::Document::on_key_down(|scancode, virtual_keycode, modifiers| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(window::Id),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DeviceId(unsafe { device::Id::dummy() }),
|
||||
input: KeyboardInput {
|
||||
scancode,
|
||||
state: ElementState::Pressed,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
backend::Document::on_key_up(|scancode, virtual_keycode, modifiers| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(window::Id),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: DeviceId(unsafe { device::Id::dummy() }),
|
||||
input: KeyboardInput {
|
||||
scancode,
|
||||
state: ElementState::Released,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Throw an exception to break out of Rust exceution and use unreachable to tell the
|
||||
// compiler this function won't return, giving it a return type of '!'
|
||||
backend::throw(
|
||||
"Using exceptions for control flow, don't mind me. This isn't actually an error!",
|
||||
);
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> Proxy<T> {
|
||||
self.elw.p.proxy()
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &root::EventLoopWindowTarget<T> {
|
||||
&self.elw
|
||||
}
|
||||
}
|
||||
19
src/platform_impl/web/event_loop/proxy.rs
Normal file
19
src/platform_impl/web/event_loop/proxy.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use super::runner;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::EventLoopClosed;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Proxy<T: 'static> {
|
||||
runner: runner::Shared<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Proxy<T> {
|
||||
pub fn new(runner: runner::Shared<T>) -> Self {
|
||||
Proxy { runner }
|
||||
}
|
||||
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||
self.runner.send_event(Event::UserEvent(event));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
216
src/platform_impl/web/event_loop/runner.rs
Normal file
216
src/platform_impl/web/event_loop/runner.rs
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
use super::{backend, state::State};
|
||||
use crate::event::{Event, StartCause};
|
||||
use crate::event_loop as root;
|
||||
|
||||
use instant::{Duration, Instant};
|
||||
use std::{cell::RefCell, clone::Clone, collections::VecDeque, rc::Rc};
|
||||
|
||||
pub struct Shared<T>(Rc<Execution<T>>);
|
||||
|
||||
impl<T> Clone for Shared<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Shared(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Execution<T> {
|
||||
runner: RefCell<Option<Runner<T>>>,
|
||||
events: RefCell<VecDeque<Event<T>>>,
|
||||
}
|
||||
|
||||
struct Runner<T> {
|
||||
state: State,
|
||||
is_busy: bool,
|
||||
event_handler: Box<dyn FnMut(Event<T>, &mut root::ControlFlow)>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Runner<T> {
|
||||
pub fn new(event_handler: Box<dyn FnMut(Event<T>, &mut root::ControlFlow)>) -> Self {
|
||||
Runner {
|
||||
state: State::Init,
|
||||
is_busy: false,
|
||||
event_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Shared<T> {
|
||||
pub fn new() -> Self {
|
||||
Shared(Rc::new(Execution {
|
||||
runner: RefCell::new(None),
|
||||
events: RefCell::new(VecDeque::new()),
|
||||
}))
|
||||
}
|
||||
|
||||
// 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
|
||||
// over a RootEventLoopWindowTarget reference
|
||||
pub fn set_listener(&self, event_handler: Box<dyn FnMut(Event<T>, &mut root::ControlFlow)>) {
|
||||
self.0.runner.replace(Some(Runner::new(event_handler)));
|
||||
self.send_event(Event::NewEvents(StartCause::Init));
|
||||
}
|
||||
|
||||
// Add an event to the event loop runner
|
||||
//
|
||||
// It will determine if the event should be immediately sent to the user or buffered for later
|
||||
pub fn send_event(&self, event: Event<T>) {
|
||||
// If the event loop is closed, it should discard any new events
|
||||
if self.closed() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if event handling is in process, and then release the borrow on the runner
|
||||
let (start_cause, event_is_start) = match *self.0.runner.borrow() {
|
||||
Some(ref runner) if !runner.is_busy => {
|
||||
if let Event::NewEvents(cause) = event {
|
||||
(cause, true)
|
||||
} else {
|
||||
(
|
||||
match runner.state {
|
||||
State::Init => StartCause::Init,
|
||||
State::Poll { .. } => StartCause::Poll,
|
||||
State::Wait { start } => StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: None,
|
||||
},
|
||||
State::WaitUntil { start, end, .. } => StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: Some(end),
|
||||
},
|
||||
State::Exit => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Events are currently being handled, so queue this one and don't try to
|
||||
// double-process the event queue
|
||||
self.0.events.borrow_mut().push_back(event);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut control = self.current_control_flow();
|
||||
// Handle starting a new batch of events
|
||||
//
|
||||
// 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
|
||||
self.handle_event(Event::NewEvents(start_cause), &mut control);
|
||||
if !event_is_start {
|
||||
self.handle_event(event, &mut control);
|
||||
}
|
||||
self.handle_event(Event::EventsCleared, &mut control);
|
||||
self.apply_control_flow(control);
|
||||
// If the event loop is closed, it has been closed this iteration and now the closing
|
||||
// event should be emitted
|
||||
if self.closed() {
|
||||
self.handle_event(Event::LoopDestroyed, &mut control);
|
||||
}
|
||||
}
|
||||
|
||||
// handle_event takes in events and either queues them or applies a callback
|
||||
//
|
||||
// It should only ever be called from send_event
|
||||
fn handle_event(&self, event: Event<T>, control: &mut root::ControlFlow) {
|
||||
let closed = self.closed();
|
||||
|
||||
match *self.0.runner.borrow_mut() {
|
||||
Some(ref mut runner) => {
|
||||
// An event is being processed, so the runner should be marked busy
|
||||
runner.is_busy = true;
|
||||
|
||||
(runner.event_handler)(event, control);
|
||||
|
||||
// Maintain closed state, even if the callback changes it
|
||||
if closed {
|
||||
*control = root::ControlFlow::Exit;
|
||||
}
|
||||
|
||||
// An event is no longer being processed
|
||||
runner.is_busy = false;
|
||||
}
|
||||
// If an event is being handled without a runner somehow, add it to the event queue so
|
||||
// it will eventually be processed
|
||||
_ => 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
|
||||
// If the runner doesn't exist and this method recurses, it will recurse infinitely
|
||||
if !closed && self.0.runner.borrow().is_some() {
|
||||
// Take an event out of the queue and handle it
|
||||
if let Some(event) = self.0.events.borrow_mut().pop_front() {
|
||||
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: root::ControlFlow) {
|
||||
let mut control_flow_status = match control_flow {
|
||||
root::ControlFlow::Poll => {
|
||||
let cloned = self.clone();
|
||||
State::Poll {
|
||||
timeout: backend::Timeout::new(
|
||||
move || cloned.send_event(Event::NewEvents(StartCause::Poll)),
|
||||
Duration::from_millis(1),
|
||||
),
|
||||
}
|
||||
}
|
||||
root::ControlFlow::Wait => State::Wait {
|
||||
start: Instant::now(),
|
||||
},
|
||||
root::ControlFlow::WaitUntil(end) => {
|
||||
let cloned = self.clone();
|
||||
let start = Instant::now();
|
||||
let delay = if end <= start {
|
||||
Duration::from_millis(0)
|
||||
} else {
|
||||
end - start
|
||||
};
|
||||
State::WaitUntil {
|
||||
start,
|
||||
end,
|
||||
timeout: backend::Timeout::new(
|
||||
move || cloned.send_event(Event::NewEvents(StartCause::Poll)),
|
||||
delay,
|
||||
),
|
||||
}
|
||||
}
|
||||
root::ControlFlow::Exit => State::Exit,
|
||||
};
|
||||
|
||||
match *self.0.runner.borrow_mut() {
|
||||
Some(ref mut runner) => {
|
||||
// Put the new control flow status in the runner, and take out the old one
|
||||
// This way we can safely take ownership of the TimeoutHandle and clear it,
|
||||
// so that we don't get 'ghost' invocations of Poll or WaitUntil from earlier
|
||||
// set_timeout invocations
|
||||
std::mem::swap(&mut runner.state, &mut control_flow_status);
|
||||
match control_flow_status {
|
||||
State::Poll { timeout } | State::WaitUntil { timeout, .. } => timeout.clear(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the event loop is currntly closed
|
||||
fn closed(&self) -> bool {
|
||||
match *self.0.runner.borrow() {
|
||||
Some(ref runner) => runner.state.is_exit(),
|
||||
None => false, // If the event loop is None, it has not been intialised yet, so it cannot be closed
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current control flow state
|
||||
fn current_control_flow(&self) -> root::ControlFlow {
|
||||
match *self.0.runner.borrow() {
|
||||
Some(ref runner) => runner.state.into(),
|
||||
None => root::ControlFlow::Poll,
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/platform_impl/web/event_loop/state.rs
Normal file
42
src/platform_impl/web/event_loop/state.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
use super::backend;
|
||||
use crate::event_loop::ControlFlow;
|
||||
|
||||
use instant::Instant;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum State {
|
||||
Init,
|
||||
WaitUntil {
|
||||
timeout: backend::Timeout,
|
||||
start: Instant,
|
||||
end: Instant,
|
||||
},
|
||||
Wait {
|
||||
start: Instant,
|
||||
},
|
||||
Poll {
|
||||
timeout: backend::Timeout,
|
||||
},
|
||||
Exit,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn is_exit(&self) -> bool {
|
||||
match self {
|
||||
State::Exit => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<State> for ControlFlow {
|
||||
fn from(state: State) -> ControlFlow {
|
||||
match state {
|
||||
State::Init => ControlFlow::Poll,
|
||||
State::WaitUntil { end, .. } => ControlFlow::WaitUntil(end),
|
||||
State::Wait { .. } => ControlFlow::Wait,
|
||||
State::Poll { .. } => ControlFlow::Poll,
|
||||
State::Exit => ControlFlow::Exit,
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/platform_impl/web/event_loop/window_target.rs
Normal file
106
src/platform_impl/web/event_loop/window_target.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
use super::{backend, device, proxy::Proxy, runner, window};
|
||||
use crate::event::{DeviceId, ElementState, Event, TouchPhase, WindowEvent};
|
||||
use crate::event_loop::ControlFlow;
|
||||
use crate::window::WindowId;
|
||||
use std::clone::Clone;
|
||||
|
||||
pub struct WindowTarget<T: 'static> {
|
||||
pub(crate) runner: runner::Shared<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for WindowTarget<T> {
|
||||
fn clone(&self) -> Self {
|
||||
WindowTarget {
|
||||
runner: self.runner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WindowTarget<T> {
|
||||
pub fn new() -> Self {
|
||||
WindowTarget {
|
||||
runner: runner::Shared::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn proxy(&self) -> Proxy<T> {
|
||||
Proxy::new(self.runner.clone())
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
&self,
|
||||
event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>,
|
||||
) -> &runner::Shared<T> {
|
||||
self.runner.set_listener(event_handler);
|
||||
&self.runner
|
||||
}
|
||||
|
||||
pub fn register(&self, canvas: &backend::Canvas) {
|
||||
let runner = &self.runner;
|
||||
|
||||
canvas.on_mouse_out(|pointer_id| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(window::Id),
|
||||
event: WindowEvent::CursorLeft {
|
||||
device_id: DeviceId(device::Id(pointer_id)),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
canvas.on_mouse_over(|pointer_id| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(window::Id),
|
||||
event: WindowEvent::CursorEntered {
|
||||
device_id: DeviceId(device::Id(pointer_id)),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
canvas.on_mouse_move(|pointer_id, position, modifiers| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(window::Id),
|
||||
event: WindowEvent::CursorMoved {
|
||||
device_id: DeviceId(device::Id(pointer_id)),
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
canvas.on_mouse_up(|pointer_id, button, modifiers| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(window::Id),
|
||||
event: WindowEvent::MouseInput {
|
||||
device_id: DeviceId(device::Id(pointer_id)),
|
||||
state: ElementState::Released,
|
||||
button,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
canvas.on_mouse_down(|pointer_id, button, modifiers| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(window::Id),
|
||||
event: WindowEvent::MouseInput {
|
||||
device_id: DeviceId(device::Id(pointer_id)),
|
||||
state: ElementState::Pressed,
|
||||
button,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
canvas.on_mouse_scroll(|pointer_id, delta, modifiers| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: WindowId(window::Id),
|
||||
event: WindowEvent::MouseWheel {
|
||||
device_id: DeviceId(device::Id(pointer_id)),
|
||||
delta,
|
||||
phase: TouchPhase::Moved,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue