Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stdweb support for eventloop 2.0 #797

Closed
wants to merge 31 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fd4db40
Create the type layout
ryanisaacg Feb 13, 2019
f44e98d
Implemented a few easy methods
ryanisaacg Feb 20, 2019
c088f8b
Create the outline of event input and handler calls
ryanisaacg Feb 26, 2019
f698d45
Add key and mouse event support that typechecks
ryanisaacg Mar 2, 2019
37d354c
Get to a state where a canvas is spawned
ryanisaacg Mar 10, 2019
283a8de
Refactor out the stdweb functionality into different modules
ryanisaacg Mar 10, 2019
3dd0e31
Merge eventloop-2.0 into stdweb-eventloop-2
ryanisaacg Mar 11, 2019
aaee724
Rearchitect to allow API compliance
ryanisaacg Mar 12, 2019
d1deba8
Rename modules
ryanisaacg Mar 12, 2019
a5166ba
Implement request_redraw
ryanisaacg Mar 16, 2019
96786bb
Implement focus event
ryanisaacg Mar 16, 2019
85446d8
Fix warnings
ryanisaacg Mar 16, 2019
b09629f
Handle ControlFlow::Exit
ryanisaacg Mar 19, 2019
7c6bdcc
Handle ControlFlow::Exit and dealing with events-in-events
ryanisaacg Mar 23, 2019
9e25561
Fix compile failures and add canvas positioning
ryanisaacg Apr 3, 2019
fe5e300
Clean up and document the core of stdweb event handling
ryanisaacg Apr 25, 2019
9f801cf
Only send the request-redraw on the next animation frame
ryanisaacg Apr 29, 2019
70c7382
Fix the request_animation_frame lifetimes
ryanisaacg May 2, 2019
37dadab
Add access to the canvas in the Window
ryanisaacg Jun 1, 2019
1409f83
Add support for mouse wheel
ryanisaacg Jun 1, 2019
f2b6ef2
Merge master into stdweb-eventloop-2
ryanisaacg Jun 1, 2019
b59e3c6
WIP
ryanisaacg Jun 15, 2019
2690306
Implement Poll and WaitUntil in the stdweb backend
ryanisaacg Jun 17, 2019
182beb4
Indicate that I will be maintaing the stdweb backend
ryanisaacg Jun 17, 2019
b571362
Fix a panic due to double-borrow
ryanisaacg Jun 21, 2019
cf28751
Remove unnecessary set-to-wait in example
ryanisaacg Jun 23, 2019
a0f280e
Update how timeouts are cleared to avoid possible double-clearing
ryanisaacg Jun 23, 2019
94387c4
Merge master Monitor APIs to stdweb
ryanisaacg Jun 27, 2019
e94a006
Merge remote-tracking branch 'upstream/master' into stdweb-eventloop-2
ryanisaacg Jul 9, 2019
7bbc829
Fix a few warnings
ryanisaacg Jul 9, 2019
c76ab65
Add on-close event listening
ryanisaacg Jul 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Rename modules
ryanisaacg committed Mar 12, 2019

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit d1deba8620127608beeb80d8e8ee4ec7e3e5e733
271 changes: 271 additions & 0 deletions src/platform_impl/stdweb/event_loop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
use super::*;

use dpi::{LogicalPosition, LogicalSize};
use event::{DeviceEvent, DeviceId as RootDI, ElementState, Event, KeyboardInput, ModifiersState, MouseButton, ScanCode, StartCause, VirtualKeyCode, WindowEvent};
use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed};
use icon::Icon;
use window::{MouseCursor, WindowId as RootWI};
use stdweb::{
JsSerialize,
traits::*,
unstable::TryInto,
web::{
document,
event::*,
html_element::CanvasElement,
},
};
use std::cell::{RefCell, RefMut};
use std::collections::VecDeque;
use std::collections::vec_deque::IntoIter as VecDequeIter;
use std::marker::PhantomData;
use std::rc::Rc;

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(i32);

impl DeviceId {
pub unsafe fn dummy() -> Self {
DeviceId(0)
}
}

pub struct EventLoop<T: 'static> {
elw: RootELW<T>,
runner: EventLoopRunnerShared<T>
}

pub struct EventLoopWindowTarget<T: 'static> {
pub(crate) canvases: RefCell<Vec<CanvasElement>>,
_marker: PhantomData<T>
}

impl<T> EventLoopWindowTarget<T> {
fn new() -> Self {
EventLoopWindowTarget {
canvases: RefCell::new(Vec::new()),
_marker: PhantomData
}
}
}

#[derive(Clone)]
pub struct EventLoopProxy<T> {
runner: EventLoopRunnerShared<T>
}

impl<T> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
self.runner.send_event(Event::UserEvent(event));
Ok(())
}
}

type EventLoopRunnerShared<T> = Rc<ELRShared<T>>;

struct ELRShared<T> {
runner: RefCell<Option<EventLoopRunner<T>>>,
events: RefCell<VecDeque<Event<T>>>, // TODO: this may not be necessary?
}

struct EventLoopRunner<T> {
control: ControlFlow,
event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>,
}

impl<T> EventLoop<T> {
pub fn new() -> Self {
EventLoop {
elw: RootELW {
p: EventLoopWindowTarget::new(),
_marker: PhantomData
},
runner: Rc::new(ELRShared::blank()),
}
}

pub fn get_available_monitors(&self) -> VecDequeIter<MonitorHandle> {
VecDeque::new().into_iter()
}

pub fn get_primary_monitor(&self) -> MonitorHandle {
MonitorHandle
}

pub fn run<F>(mut self, mut event_handler: F) -> !
where F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow)
{
// TODO: Create event handlers for the JS events
// TODO: how to handle request redraw?
// TODO: onclose (stdweb PR)
// TODO: file dropping, PathBuf isn't useful for web
let EventLoop { elw, runner } = self;
for canvas in elw.p.canvases.borrow().iter() {
register(&runner, canvas);
}
let relw = RootELW {
p: EventLoopWindowTarget::new(),
_marker: PhantomData
};
runner.set_listener(Box::new(move |evt, ctrl| event_handler(evt, &relw, ctrl)));

let document = &document();
add_event(&runner, document, |_, _: BlurEvent| {
});
add_event(&runner, document, |_, _: FocusEvent| {

});
add_event(&runner, document, |elrs, event: KeyDownEvent| {
let key = event.key();
let mut characters = key.chars();
let first = characters.next();
let second = characters.next();
if let (Some(key), None) = (first, second) {
elrs.send_event(Event::WindowEvent {
window_id: RootWI(WindowId),
event: WindowEvent::ReceivedCharacter(key)
});
}
elrs.send_event(Event::WindowEvent {
window_id: RootWI(WindowId),
event: WindowEvent::KeyboardInput {
// TODO: is there a way to get keyboard device?
device_id: RootDI(unsafe { DeviceId::dummy() }),
input: KeyboardInput {
scancode: scancode(&event),
state: ElementState::Pressed,
virtual_keycode: button_mapping(&event),
modifiers: keyboard_modifiers_state(&event),
}
}
});
});
add_event(&runner, document, |elrs, event: KeyUpEvent| {
elrs.send_event(Event::WindowEvent {
window_id: RootWI(WindowId),
event: WindowEvent::KeyboardInput {
// TODO: is there a way to get keyboard device?
device_id: RootDI(unsafe { DeviceId::dummy() }),
input: KeyboardInput {
scancode: scancode(&event),
state: ElementState::Released,
virtual_keycode: button_mapping(&event),
modifiers: keyboard_modifiers_state(&event),
}
}
});
});
stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here?
js! {
throw "Using exceptions for control flow, don't mind me";
}
unreachable!();
}

pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
runner: self.runner.clone()
}
}

pub fn window_target(&self) -> &RootELW<T> {
&self.elw
}
}

fn register<T: 'static>(elrs: &EventLoopRunnerShared<T>, canvas: &CanvasElement) {
add_event(elrs, canvas, |elrs, event: PointerOutEvent| {
elrs.send_event(Event::WindowEvent {
window_id: RootWI(WindowId),
event: WindowEvent::CursorLeft {
device_id: RootDI(DeviceId(event.pointer_id()))
}
});
});
add_event(elrs, canvas, |elrs, event: PointerOverEvent| {
elrs.send_event(Event::WindowEvent {
window_id: RootWI(WindowId),
event: WindowEvent::CursorEntered {
device_id: RootDI(DeviceId(event.pointer_id()))
}
});
});
add_event(elrs, canvas, |elrs, event: PointerMoveEvent| {
elrs.send_event(Event::WindowEvent {
window_id: RootWI(WindowId),
event: WindowEvent::CursorMoved {
device_id: RootDI(DeviceId(event.pointer_id())),
position: LogicalPosition {
x: event.offset_x(),
y: event.offset_y()
},
modifiers: mouse_modifiers_state(&event)
}
});
});
add_event(elrs, canvas, |elrs, event: PointerUpEvent| {
elrs.send_event(Event::WindowEvent {
window_id: RootWI(WindowId),
event: WindowEvent::MouseInput {
device_id: RootDI(DeviceId(event.pointer_id())),
state: ElementState::Pressed,
button: mouse_button(&event),
modifiers: mouse_modifiers_state(&event)
}
});
});
add_event(elrs, canvas, |elrs, event: PointerDownEvent| {
elrs.send_event(Event::WindowEvent {
window_id: RootWI(WindowId),
event: WindowEvent::MouseInput {
device_id: RootDI(DeviceId(event.pointer_id())),
state: ElementState::Released,
button: mouse_button(&event),
modifiers: mouse_modifiers_state(&event)
}
});
});
}

fn add_event<T: 'static, E, F>(elrs: &EventLoopRunnerShared<T>, target: &impl IEventTarget, mut handler: F)
where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared<T>, E) + 'static {
let elrs = elrs.clone(); // TODO: necessary?

target.add_event_listener(move |event: E| {
event.prevent_default();
event.stop_propagation();
event.cancel_bubble();

handler(&elrs, event);
});
}

impl<T> ELRShared<T> {
fn blank() -> ELRShared<T> {
ELRShared {
runner: RefCell::new(None),
events: RefCell::new(VecDeque::new())
}
}

fn set_listener(&self, event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>) {
*self.runner.borrow_mut() = Some(EventLoopRunner {
control: ControlFlow::Poll,
event_handler
});
}

// TODO: handle event loop closures
// TODO: handle event buffer
fn send_event(&self, event: Event<T>) {
match *self.runner.borrow_mut() {
Some(ref mut runner) => {
// TODO: bracket this in control flow events?
(runner.event_handler)(event, &mut runner.control);
}
None => ()
}
}

}

Loading